3rd Python函數(shù)基礎(chǔ)及進(jìn)階
3.1 上章補(bǔ)充內(nèi)容
3.1.1 Bytes類(lèi)型
計(jì)算機(jī)的數(shù)據(jù)需要存到硬盤(pán)上,但是硬盤(pán)只能存儲(chǔ)二進(jìn)制的數(shù)據(jù)。
我們知道將計(jì)算機(jī)里的數(shù)據(jù)轉(zhuǎn)換成我們能看懂的數(shù)據(jù)是將二進(jìn)制 -> 十進(jìn)制(ASCII、gbk、utf-8等),那存儲(chǔ)就是反過(guò)來(lái)的過(guò)程。
文字 -> utf-8、GBK -> 2進(jìn)制
圖片 -> png、jpg -> 2進(jìn)制
音樂(lè) -> mp3、wav -> 2進(jìn)制
視頻 -> mp4、mov -> 2進(jìn)制
Python中encode()
可以用來(lái)編碼(解碼是decode()
),比如我們將'朱明'
通過(guò)utf-8編碼('朱明'.encode('utf-8')
),得到一串16進(jìn)制的數(shù)據(jù)(b'\xe6\x9c\xb1\xe6\x98\x8e'
),那這里的\xe5
就是一個(gè)字節(jié)(前面的b就表示是bytes類(lèi)型),總共6個(gè)字節(jié),我們前面說(shuō)過(guò)utf-8中一個(gè)中文字符占三個(gè)字節(jié),這里也符合這一規(guī)定。
那我們就可以來(lái)定義bytes
類(lèi)型了。bytes
類(lèi)型,以16進(jìn)制形式表示,2個(gè)16進(jìn)制數(shù)構(gòu)成一個(gè)byte,以b''
來(lái)表示。
那么問(wèn)題來(lái)了,之前我們open()
操作文件的時(shí)候都沒(méi)有涉及到二進(jìn)制,這不是相互矛盾么?
其實(shí)open()
函數(shù)是默認(rèn)幫我們進(jìn)制轉(zhuǎn)換,我們可以通過(guò)encoding='..'
來(lái)設(shè)置編碼的方式,Python3默認(rèn)的編碼是encoding='utf-8'
(pycharm加載文件的編碼也是utf-8
,有時(shí)候查看文件也可能會(huì)產(chǎn)生亂碼),我們有時(shí)產(chǎn)生文件亂碼的情況,很大可能就是編碼混亂的問(wèn)題。
問(wèn)題又來(lái)了,我們?nèi)绻M(jìn)行兩次編碼,會(huì)產(chǎn)生什么結(jié)果?
?f = open('test.txt', 'w', encoding='utf-8')
?f.write('朱明'.encode('utf-8'))
?f.close()
?# 報(bào)錯(cuò)TypeError: write()參數(shù)類(lèi)型不支持bytes,必須是str
那么Python的文件打開(kāi)形式又給我們提供了三個(gè)文件打開(kāi)方法:wb
、rb
、ab
。都是以二進(jìn)制來(lái)寫(xiě)、讀、追加。注意,這時(shí)open()
的encoding=''
就禁止傳入?yún)?shù)了(會(huì)拋出ValueError
),后面寫(xiě)入文件的數(shù)據(jù)必須是bytes
類(lèi)型的數(shù)據(jù)了,可以同時(shí)使用不同的編碼寫(xiě)入,python不會(huì)報(bào)錯(cuò),但是你查看文件的時(shí)候,解釋器會(huì)顯示亂碼的。
?f = open('test.txt', 'wb')
?f.write('朱明'.encode('utf-8'))
?# f.write('朱明'.encode('gbk'))
?f.close()
bytes
有兩個(gè)場(chǎng)景:字符、圖片、視頻等數(shù)據(jù)存硬盤(pán)的時(shí)候,數(shù)據(jù)需要變成bytes
(上面說(shuō)的就是這個(gè)場(chǎng)景);網(wǎng)絡(luò)傳輸數(shù)據(jù)時(shí),數(shù)據(jù)也需要變成bytes
類(lèi)型,才可以進(jìn)行傳輸,這點(diǎn)在講網(wǎng)絡(luò)編程時(shí)我們?cè)僬f(shuō)。
3.1.2 深淺copy
列表、字典、集合三個(gè)數(shù)據(jù)類(lèi)型都有copy()
的方法,我們以字典舉例
?# 首先我們先定義一個(gè)數(shù)據(jù)集
?data = {
? ? ?"name": "zm",
? ? ?"age": 18,
? ? ?"scores":{
? ? ? ? ?"語(yǔ)文": 110,
? ? ? ? ?"高數(shù)": 130,
? ? ? ? ?"英語(yǔ)": 98,
? ? ?},
?}
我們復(fù)制一個(gè)新的a = data
(按照前面介紹的字符數(shù)字等數(shù)據(jù)類(lèi)型復(fù)制的原理,我們類(lèi)比理解一下),a
和data
的id()
都一樣,那我們修改一下原來(lái)的data數(shù)據(jù)
,a
應(yīng)該不會(huì)改變。我們data['name'] = 'wzw'
,輸出a和data,我們發(fā)現(xiàn)兩個(gè)值都發(fā)生了變化。
那我們來(lái)剖析一下原理,字典、列表、集合這些數(shù)據(jù)類(lèi)型是一個(gè)容器(容器有一個(gè)地址),里面的數(shù)據(jù)也分別有自己的地址(和容器地址沒(méi)關(guān)聯(lián)),所以我們修改容器里面的值,a
和data
兩個(gè)指針指向都沒(méi)變,還是指向容器地址,共享同一份數(shù)據(jù)。二字符串那些不可變數(shù)據(jù)類(lèi)型修改一下值就會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù),所以有指針的指向會(huì)發(fā)生變化。
那特定情況下,比如數(shù)據(jù)備份,我們不想讓a
和data
同時(shí)改變,那就用到了copy()
方法。我們b = data.copy()
,這時(shí)候b
就是新的數(shù)據(jù)容器了,具體可以自己修改值試試。
But,當(dāng)我們修改第二層以及更深層數(shù)據(jù)時(shí),我們發(fā)現(xiàn)copy()
出來(lái)的數(shù)據(jù)和原來(lái)的數(shù)據(jù)還是會(huì)一起被修改。原因和上面類(lèi)似,我們copy()
出來(lái)的新字典中嵌套的子字典存放的又是容器地址,這樣我們改容器里面的容器里面的值就還是會(huì)和原來(lái)的值同步修改。
如果我們直接修改容器地址那就不會(huì)出現(xiàn)這樣的情況:
?b['scores'] = 22
?print(b['scores']) # 22
?print(data['scores']) # {'語(yǔ)文': 110, '高數(shù)': 130, '英語(yǔ)': 98, '數(shù)學(xué)': 130}
同理,我們可以推導(dǎo)理解三層嵌套結(jié)構(gòu),四層嵌套結(jié)構(gòu)......等等等等。
那我們就知道了,copy()
只能真正copy()
一層,我們稱(chēng)為淺copy()
。
如果我們想完完全全的copy()
嵌套數(shù)據(jù),我們就需要使用copy模塊
的deepcopy()
?import copy
?c = data.deepcopy() ?# 完全深copy
PS:深copy
比較占內(nèi)存,不常用,但是在特定的場(chǎng)景下需要用到,所以必須得知道這個(gè)東西和怎么深copy
。
3.1.3 編碼轉(zhuǎn)換
編碼與解碼
其實(shí)前面多多少少也提到了,我們這里就再簡(jiǎn)單總結(jié)說(shuō)下。
Python中編碼的方法就是encode()
,解碼的方法是decode()
,編碼出來(lái)的東西是給機(jī)器看的,返回的是bytes類(lèi)型;而解碼出來(lái)的是我們?nèi)四芸炊念?lèi)型,象數(shù)字、字符、列表等等數(shù)據(jù)類(lèi)型。
?print('朱明'.encode('utf-8')) ?# 相應(yīng)的16進(jìn)制碼
?print('朱明'.encode('utf-8').decode('gbk')) # 亂碼
?print('朱明'.encode('utf-8').decode('utf-8')) ?# 朱明
decode()
參數(shù)不寫(xiě)時(shí),默認(rèn)使用Python3默認(rèn)的編碼格式(utf-8
),建議還是寫(xiě)一下,不然有時(shí)候會(huì)不好進(jìn)行后期的debug
。
編碼轉(zhuǎn)換
那問(wèn)題來(lái)了,我們想把utf-8編碼
格式的數(shù)據(jù)轉(zhuǎn)換成gbk編碼
格式的數(shù)據(jù),比如我們要把Windows文件發(fā)到Linux上或者M(jìn)ac上,這時(shí)候就會(huì)產(chǎn)生亂碼,因?yàn)閃indows的文件解碼方式默認(rèn)是gbk
,Linux和Mac的文件解碼方式默認(rèn)是utf-8
,當(dāng)然word那些應(yīng)用會(huì)自動(dòng)幫我們解決這個(gè)問(wèn)題,如果用記事本這些軟件,那就會(huì)產(chǎn)生亂碼。那我們就需要進(jìn)行編碼的轉(zhuǎn)換(或者利用有的軟件有修改編碼的接口來(lái)解決這一問(wèn)題)。
那我們就必須要有utf-8和gbk的映射關(guān)系,那就想到了我們Unicode萬(wàn)國(guó)碼。我們就可以通過(guò)utf-8 -> unicode -> gbk來(lái)實(shí)現(xiàn)編碼的轉(zhuǎn)換。那我們底層程序來(lái)實(shí)現(xiàn)就要通過(guò)先二進(jìn)制讀取文件數(shù)據(jù),然后解碼,再用另一種格式編碼,最后將存入新文件。
?f = open('test_utf8.txt', 'rb', encoding='utf-8')
?data = f.read()
?print(type(data))
?f.close()
?# 先將utf-8編碼解碼成Unicode,再編碼成gbk
?data_gbk = data.decode('utf-8').encode('gbk')
?
?f_new = open('test_gbk.txt', 'wb', encoding='gbk')
?f_new.write(data_gbk)
?f_new.close()
3.2 函數(shù)基礎(chǔ)
3.2.1 函數(shù)定義及特性
函數(shù)的作用
函數(shù)最大的作用就是減少重復(fù)的代碼,將需要重復(fù)的代碼封裝成函數(shù),供其他不同代碼調(diào)用;而且如果實(shí)現(xiàn)功能后期需要修改的話(huà),只要修改函數(shù)就可以了,不需要修改每個(gè)模塊的代碼,減少了后期維護(hù)的麻煩(易維護(hù)),增加了程序的可擴(kuò)展性。
函數(shù)的定義語(yǔ)法
Python中稱(chēng)函數(shù)為function
,Java中為method
。
?def 函數(shù)名(各種參數(shù)):
? ? ?# 函數(shù)的功能
? ? ?pass
? # 函數(shù)可以有返回值,也可以沒(méi)有返回值
? ? ?# return ...
? ? ?# return不但可以用來(lái)返回結(jié)果,也代表函數(shù)結(jié)束的語(yǔ)句,類(lèi)似循環(huán)的break
注意的點(diǎn)
函數(shù)名我們建議小寫(xiě)
用
_
來(lái)分開(kāi)幾個(gè)單詞,比如學(xué)生人數(shù)計(jì)數(shù):count_stu()
等函數(shù)分有返回值(
return
)的和無(wú)返回值的兩種。函數(shù)外部的代碼想獲取函數(shù)的執(zhí)行結(jié)果,就可以用return
語(yǔ)句來(lái)把函數(shù)結(jié)果返回。函數(shù)執(zhí)行過(guò)程中,只要執(zhí)行到
return
語(yǔ)句,就會(huì)停止執(zhí)行,并返回結(jié)果。無(wú)返回值的函數(shù),默認(rèn)
return None
。
3.2.2 函數(shù)的參數(shù)類(lèi)型
形參和實(shí)參
?# 這里的x和y就屬于形參
?def calc(x, y):
? ? ?res = x**y
? ? ?return res
?
?# 這里的c就屬于實(shí)參
?c = calc(a, b)
?print(c)
形參只有在被調(diào)用時(shí)才分配內(nèi)存單元,調(diào)用結(jié)束之后就釋放??梢哉f(shuō),形參只在函數(shù)內(nèi)部有效,函數(shù)調(diào)用結(jié)束返回后,就不能再使用該形參變量。
實(shí)參就可以是常量、變量、表達(dá)式等等形式的參數(shù),他們必須要有確定的值,以便調(diào)用函數(shù)時(shí),把值傳給形參
位置參數(shù)
拿上面的calc(x, y)
函數(shù)為例,其中的x和y就是calc(x, y)
的兩個(gè)位置參數(shù),如果我們calc(1)
,少傳入一個(gè)參數(shù),python就會(huì)報(bào)錯(cuò)少一個(gè)positional argument。
函數(shù)的開(kāi)發(fā)規(guī)范
每個(gè)函數(shù)下面都需要加上函數(shù)每個(gè)參數(shù)的意思和需要傳入的參數(shù)類(lèi)型等信息,Pycharm中,我們可以打三個(gè)雙引號(hào),然后回車(chē)就會(huì)自動(dòng)生成,然后我們就可以在每行冒號(hào)后面寫(xiě)上參數(shù)的信息,生成類(lèi)似如下代碼的樣子。
?def register(name, age, major, country):
? ? ?"""
? ? ?:param name:str ? ? 用戶(hù)姓名
? ? ?:param age:int ? ? ?用戶(hù)年齡
? ? ?:param major:str ? ?用戶(hù)學(xué)科
? ? ?:param country:str ?用戶(hù)國(guó)籍
? ? ?:return:
? ? ?"""
? ? ?info = f"""
? ? ?--------------- 你的注冊(cè)信息 --------------
? ? ?name:{name}
? ? ?age:{age}
? ? ?major:{major}
? ? ?country:{country}
? ? ?"""
? ? ?print(info)
?
?
?register('zm', 18, 'math', 'China')
默認(rèn)參數(shù)
那我們?cè)O(shè)想一個(gè)情況,該程序大多數(shù)都是中國(guó)國(guó)內(nèi)的人使用,所以country
這個(gè)位置參數(shù)就大概率是China,我們就可以采用默認(rèn)參數(shù),然后設(shè)置成China,然后你不傳入這個(gè)位置的參數(shù)就是默認(rèn)country是China,傳入就用你傳入的信息。
?def register(name, age, major, country='China'):
? ? ?"""
? ? ?:param name:str ? ? 用戶(hù)姓名
? ? ?:param age:int ? ? ?用戶(hù)年齡
? ? ?:param major:str ? ?用戶(hù)學(xué)科
? ? ?:param country:str ?用戶(hù)國(guó)籍
? ? ?:return:
? ? ?"""
? ? ?info = f"""
? ? ?--------------- 你的注冊(cè)信息 --------------
? ? ?name:{name}
? ? ?age:{age}
? ? ?major:{major}
? ? ?country:{country}
? ? ?"""
? ? ?print(info)
?
?
?register('zm', 18, 'math') # country值為China
?register('wzw', 18, 'math', 'US') # country值為US
不過(guò),需要注意的是:默認(rèn)參數(shù)必須寫(xiě)在函數(shù)參數(shù)的最末端,多個(gè)默認(rèn)參數(shù)也是一樣,都放在后面就好,比如calc(x, y, z='1', b='2')
。不然就會(huì)報(bào)錯(cuò)SyntaxError: non-default argument follows default argument
。
關(guān)鍵參數(shù)
通過(guò)上面的函數(shù)參數(shù)的傳入,我們可以知道,函數(shù)的傳參必須按順序傳入,不然本來(lái)賦給name
的參數(shù)賦給了age
,那就會(huì)導(dǎo)致函數(shù)后續(xù)的功能產(chǎn)生一些錯(cuò)誤,導(dǎo)致最后的結(jié)果錯(cuò)誤。
那我們可以使用關(guān)鍵參數(shù)來(lái)亂序傳參。
?def register(name, age, major, country='China'):
? ? ?"""
? ? ?:param name:str ? ? 用戶(hù)姓名
? ? ?:param age:int ? ? ?用戶(hù)年齡
? ? ?:param major:str ? ?用戶(hù)學(xué)科
? ? ?:param country:str ?用戶(hù)國(guó)籍
? ? ?:return:
? ? ?"""
? ? ?info = f"""
? ? ?--------------- 你的注冊(cè)信息 --------------
? ? ?name:{name}
? ? ?age:{age}
? ? ?major:{major}
? ? ?country:{country}
? ? ?"""
? ? ?print(info)
?
?
?register(major='math', age=18, name='wzw') # 沒(méi)有出現(xiàn)亂序
那我們可以關(guān)鍵參數(shù)和位置參數(shù)混合傳入?yún)?shù)么?
答案是可以,不過(guò)有兩個(gè)需要注意的點(diǎn):
調(diào)用函數(shù)傳參時(shí),位置參數(shù)必須在關(guān)鍵參數(shù)的前面,比如
register(major='math', 18, name='wzw')
就·會(huì)報(bào)錯(cuò)SyntaxError: positional argument follows keyword argument
很容易產(chǎn)生參數(shù)傳入錯(cuò)誤。比如
register('zm', major='math', age=18, name='wzw')
,運(yùn)行時(shí),Python就會(huì)報(bào)錯(cuò)TypeError: register() got multiple values for argument 'name'
,意思就是有多個(gè)參數(shù),傳入了name這個(gè)位置。
所以我們建議盡量用一種,不要混在一起使用,既對(duì)自己寫(xiě)代碼造成一些困擾,又給你后期維護(hù)帶來(lái)很多不便。
非固定參數(shù)
現(xiàn)在我們需要寫(xiě)一個(gè)累加的函數(shù),簡(jiǎn)單一點(diǎn),我們規(guī)定傳入的參數(shù)必須是整形數(shù)字,返回所以傳入?yún)?shù)的累加值。
?def num_sum(a, b):
? ? ?return a+b
?
?print(num_sum(1, 2)) # 3
這個(gè)函數(shù)只能支持兩個(gè)參數(shù),不能靈活的接收多個(gè)不固定的參數(shù),那我們就可以這樣寫(xiě):
?def num_sum(*args):
? ? ?return sum(args)
?
?
?print(num_sum(1, 1, 1, 1, 1, 1, 1, 1)) # 8
我們?cè)诤瘮?shù)最后面(存在默認(rèn)參數(shù)的話(huà),就寫(xiě)在默認(rèn)參數(shù)的后面),添加*args
或者**kwargs
(args
就是arguments的簡(jiǎn)寫(xiě))
那*args
和**kwargs
的區(qū)別是什么呢?
我們可以輸出一下
?def name(*args, **kwargs):
? ? ?print(args, kwargs)
? ? ?
?name() ?# 輸出結(jié)果:() {}
我們可以看出,一個(gè)是元組形式,一個(gè)是字典形式。元組形式的用來(lái)接受多個(gè)位置參數(shù)的傳入;字典形式的用來(lái)接受多個(gè)關(guān)鍵參數(shù)的傳入。
那有人可能會(huì)問(wèn),我把上面非固定參數(shù)、位置參數(shù)、關(guān)鍵參數(shù)可以一起用嗎?
答案是可以,不過(guò)還是一樣的問(wèn)題,那就是參數(shù)傳入的混亂,所以實(shí)際開(kāi)發(fā)需要盡量避免混合使用,而且出現(xiàn)函數(shù)必須使用混合使用不同類(lèi)型參數(shù)的情況幾乎沒(méi)有,有的話(huà),也是簡(jiǎn)單的混合。
3.2.3 函數(shù)引出的變量問(wèn)題
?name = 'zm'
?def change():
? ? ?name = 'wzw'
? ? ?print(name)
? ? ?
?def change_2():
? ? ?print(name)
? ? ?
?change() # 輸出wzw
?print(name) # 輸出zm -> change()沒(méi)有改變第一個(gè)name的值
?change_2() # 輸出zm
?
?# 為什么change()沒(méi)有改變name一開(kāi)始的值(name)呢?
?# 因?yàn)榈谝粋€(gè)name和change()內(nèi)部自己定義的name不是一個(gè)變量。一個(gè)是全部代碼都可以調(diào)用的全局變量,另一個(gè)是函數(shù)內(nèi)被的局部變量。Python規(guī)定函數(shù)是不能直接改變?nèi)肿兞康摹?/span>
全部變量
能夠被全部的代碼調(diào)用的變量就是全局變量。上面代碼中的第一個(gè)name
就是全局變量。
局部變量
在函數(shù)中定義的變量就是局部變量,只能供所在函數(shù)內(nèi)部調(diào)用。函數(shù)內(nèi)部變量的查找順序是局部變量>全局變量,所以change()
輸出自己的局部變量name
,change_2()
因?yàn)檎也坏阶约河?code>name這個(gè)局部變量,才輸出全局變量name
的。
在函數(shù)中修改全局變量
用global
聲明就好了
?name = 'zm'
?def change():
? ? ?global name # 聲明函數(shù)內(nèi)部的name是全局變量name
? ? ?name = 'wzw'
? ? ?print(name)
?
?print(name) # 輸出wzw,而不是zm了
但不建議使用,因?yàn)楹笃诰S護(hù)的話(huà),不好找全局變量,不符合開(kāi)發(fā)規(guī)范。
查看兩種變量的方法
python有兩個(gè)內(nèi)置方法:locals()
和globals()
,分別可以查看局部變量和全局變量。局部變量返回的是調(diào)用位置的局部變量,而不是全部函數(shù)的局部變量。全局變量不管在哪調(diào)用都是返回全部的全局變量。
3.2.4 函數(shù)是否能修改可變變量
?names = ['zm', 'wzw']
?def change():
? ? ?names.append('ghd')
? ? ?# names = ['zm'] 重新定義names則會(huì)報(bào)錯(cuò),比不可變的全局變量更激進(jìn)(不可變變量可以同時(shí)存在局部變量和全局變量(比如外面的name和函數(shù)內(nèi)部的name);而可變變量不能同時(shí)存在局部變量和全局變量(外面有了names,函數(shù)里面再定義一個(gè)names就報(bào)錯(cuò)))
?print(names)
通過(guò)上面的代碼,我們可以看出,函數(shù)是可以改變可變變量(列表、字典、集合等等)的。
原理就是第一章我們講到的變量的原理,只是我們從字符串和數(shù)字的不可變的變量延伸到我們這里的可變的變量(list
、dict
、set
等數(shù)據(jù)類(lèi)型)。函數(shù)獲取到的names只是容器的地址,函數(shù)不可以修改這個(gè)容器(比如names = ['zm']
就會(huì)報(bào)錯(cuò));但是函數(shù)是可以修改容器里面的東西(包括元素、鍵值對(duì)在容器內(nèi)的東西)的,比如函數(shù)里面的names.append('ghd')
就可以在全局變量names里面增加元素。
3.2.5 嵌套&匿名&高階函數(shù)
嵌套函數(shù)
?name = 'ghd'
?def change():
? ? ?name = 'zm'
? ? ?
? ? ?def change2():
? ? ? ? ?name = 'wzw'
? ? ? ? ?print('第三層打印'+name)
? ? ?change2()
? ? ?print('第二層打印'+name)
?print('最外層(第一層)'+name)
?
?# 輸出:
?# 第三層打印wzw
?# 第二層打印zm
?# 最外層(第一層)ghd
等到后面的裝飾器就會(huì)用到嵌套函數(shù)的知識(shí)。
匿名函數(shù)
匿名函數(shù)就是不起名字的函數(shù),像上面的change和change2都是函數(shù)的名稱(chēng)。
匿名函數(shù)的語(yǔ)法是lambda
,例子如下:
?def calc(x, y):
? ? ?return x**y
?
?print(calc(2, 5))
?# 為了好理解,我們給匿名函數(shù)起個(gè)名字c,方便后面調(diào)用
?c = lambda x, y: x**y
?print(calc(2, 5))
匿名函數(shù)可以縮短代碼量,但是也比較令人不好理解。lambda
的匿名函數(shù)通常搭配其他函數(shù)使用,比如map()
:
?# map()可以放兩個(gè)參數(shù),一個(gè)是函數(shù),一個(gè)是列表、字典等可迭代的數(shù)據(jù)
?res = map(lambda x: x**2, [1, 5, 7, 4, 8])
?for i in res:
? ? ?print(i)
?# 輸出結(jié)果:
?# 1\n25\n49\n16\n64\n
上面的代碼也可以寫(xiě)成一個(gè)函數(shù),我們假設(shè)是calc(x)
,然后map(calc, [1, 5, 7, 4, 8])
,匿名函數(shù)這樣就顯得很簡(jiǎn)便,同時(shí)代碼的逼格也就提高了。
不過(guò),lambda
最多也就可以寫(xiě)一個(gè)三元運(yùn)算(加一個(gè)簡(jiǎn)單的if
語(yǔ)句,詳看Python基礎(chǔ)語(yǔ)法的筆記2.3),一些帶for
等等較復(fù)雜的函數(shù),就不好轉(zhuǎn)換成lambda
寫(xiě)。
?# lambda的三元運(yùn)算
?res = map(lambda x: x**2 if x > 5 else x**3, [1, 5, 7, 4, 8])
?for i in res:
? ? ?print(i)
上面寫(xiě)的匿名函數(shù)的意思是,如果x大于5就平方,否則就三次方。輸出的結(jié)果是
?1 # 1^3
?125 # 5^3
?49 # 7^2
?64 # 4^3
?64 # 8^2
高階函數(shù)
我們現(xiàn)在知道,可以傳入的參數(shù)有字符、數(shù)字、list、dict、set等等,那我們是否可以使函數(shù)作為參數(shù)傳入呢?
答案是可以的,而且上面的map()
也用到了(傳入的第一個(gè)參數(shù)是函數(shù))。那我們也來(lái)寫(xiě)一個(gè)函數(shù)作為參數(shù)傳入的函數(shù)。
?# 絕對(duì)值
?def get_abs(n):
? ? ?return int(str(n).strip('-'))
?
?# 兩個(gè)數(shù)的絕對(duì)值的和
?def sum_abs(x, y, f):
? ? ?return f(x) + f(y)
?
?print(sum_abs(-1, 3, get_abs))
那這個(gè)平常比較少用到,都是為了后面學(xué)習(xí)裝飾器做鋪墊。
3.2.6 遞歸函數(shù)
我們現(xiàn)在有一個(gè)奇怪的需求,一個(gè)數(shù),然后不斷除2(出現(xiàn)小數(shù)就向下取整),然后輸出每次運(yùn)算出的整型。
?n = 100
?while n>0:
? ? ?n = int(n/2)
? ? ?print(n)
那我們現(xiàn)在需要把這個(gè)改成函數(shù),我們可以這樣寫(xiě):
?def calc(n):
? ? ?n = int(n/2)
? ? ?print(n)
? ? ?if n > 0:
? ? ? calc(n)
? ? ?# print(n) 下面解釋
?calc(100)
?
?# 輸出:
?# 50
?# 25
?# 12
?# 6
?# 3
?# 1
?# 0
這樣的函數(shù)就叫做遞歸函數(shù),可以近似理解為函數(shù)循環(huán)。那大家也注意到了,代碼中有一個(gè)注釋的語(yǔ)句,我們把它解除注釋?zhuān)缓髨?zhí)行一次,我們發(fā)現(xiàn)輸出的東西是從100到0又到100(50\n25\n12\n6\n3\n1\n0\n0\n1\n3\n6\n12\n25\n50
),我們可以來(lái)解釋一下這個(gè)現(xiàn)象。
首先,第一次遞歸得到的結(jié)果是50,我們第一次輸出過(guò)后,進(jìn)入判斷n>0
,然后進(jìn)入了第二次遞歸,第一次遞歸的第二次輸出就暫緩執(zhí)行,等待第二次遞歸結(jié)束再執(zhí)行。那么,以此類(lèi)推,當(dāng)我們執(zhí)行第n次遞歸的時(shí)候,前面有n-1個(gè)遞歸在等待結(jié)果,只有最后if條件不滿(mǎn)足,最后以此遞歸結(jié)束,前面的遞歸才倒序依次結(jié)束。
那我們就可以看出,遞歸有什么缺點(diǎn)了,那就是占內(nèi)存,效率不高,還可能會(huì)導(dǎo)致出現(xiàn)棧溢出的現(xiàn)象,我們接下來(lái)總結(jié)一下遞歸的一些注意點(diǎn)。
遞歸的特點(diǎn):
必須有一個(gè)明確的結(jié)束條件。(不然遞歸就會(huì)一直進(jìn)行下去)
每次進(jìn)入更深一層遞歸時(shí),問(wèn)題規(guī)模應(yīng)當(dāng)比上次遞歸小
遞歸的效率不高,遞歸層次過(guò)多會(huì)導(dǎo)致棧溢出。(計(jì)算機(jī)中,函數(shù)調(diào)用是通過(guò)棧(
stack
)這個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)實(shí)現(xiàn)的,每當(dāng)進(jìn)入一個(gè)函數(shù)調(diào)用,棧就會(huì)加一層棧幀,每當(dāng)函數(shù)返回,棧就減少一層。由于棧的大小不是無(wú)限的,所以遞歸調(diào)用的次數(shù)過(guò)多就會(huì)導(dǎo)致棧溢出的現(xiàn)象)
Python限制遞歸次數(shù)最多994次,是可以改的,有特殊需求可以自行百度修改。棧很重要,是一個(gè)先進(jìn)后出的數(shù)據(jù)結(jié)構(gòu),想進(jìn)一步理解可以百度或者學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)。
當(dāng)然,遞歸在特定情況也是有很多應(yīng)用的,比如以后學(xué)的一些算法,比如堆排、快排,這些比較復(fù)雜,我們以后再說(shuō)。
遞歸練習(xí)——2分查找
用遞歸實(shí)現(xiàn)2分查找的算法,以列表a = [1, 3, 4, 6, 7, 8, 9, 11, 15, 17, 19, 21, 22, 25, 29, 33, 38, 69, 107]
為例,查找指定的值。
?# 這里是我實(shí)現(xiàn)的方案
?a = [1, 3, 4, 6, 7, 8, 9, 11, 15, 17, 19, 21, 22, 25, 29, 33, 38, 69, 107]
?temp = 0
?
?def half_find(list_test, n):
? ? ?global temp
? ? ?mid = int(len(list_test)/2)
?
? ? ?if n < list_test[mid]:
? ? ? ? ?half_find(list_test[:mid+1], n)
? ? ?elif n == list_test[mid]:
? ? ? ? ?temp = mid
? ? ?else:
? ? ? ? ?half_find(list_test[mid:], n)
?
? ? ?return temp
?
?print(half_find(a, 4))
?print(a.index(4))
3.2.7 Python的內(nèi)置函數(shù)
所謂內(nèi)置函數(shù),就是Python自己自帶的函數(shù),比如print()
、id()
、map()
、input()
等等,我們這里就集中來(lái)說(shuō)說(shuō)Python的常用內(nèi)置函數(shù)。
Python的所有內(nèi)置函數(shù)如下圖所示:

下面我們就一一做一些講解,有個(gè)印象就行,用到就再找就好。
abs()
:求絕對(duì)值ascii()
:返回傳入?yún)?shù)的ascii碼,比如ascii('中國(guó)')
,返回是\\u4e2d\\u56fd
bin()
:二進(jìn)制,傳入數(shù)字,將數(shù)字轉(zhuǎn)換成二進(jìn)制返回bool()
:空列表、空字典、空集合、0都代表False,其他都代表True的,可以傳入?yún)?shù),看看他們的返回值all()
:傳入的參數(shù)必須可以遍歷,我們稱(chēng)為可迭代對(duì)象,all()
會(huì)判斷這個(gè)可迭代對(duì)象內(nèi)的所有元素套一個(gè)bool()
,看它們是否都為T(mén)rue。一些情況下,我們還是需要這個(gè)函數(shù)的。any()
:和all()
類(lèi)似的原理,all()
是全部是True才會(huì)返回True,any()
是只要有一個(gè)是True就返回True。bytearray()
:將二進(jìn)制轉(zhuǎn)換成二進(jìn)制列表。字符串是不可變的,我們將其變?yōu)?code>bytes類(lèi)型,bytes
類(lèi)型也不可修改,然后我們?cè)趯?code>bytes類(lèi)型改為bytearray
類(lèi)型,然后我們就可以按照索引修改對(duì)應(yīng)的bytes
類(lèi)型數(shù)據(jù),然后bytearray()
可以直接decode()
,就變成了新的字符。bytes()
:比如bytes('中國(guó)', 'utf-8')
,和'中國(guó)'.encode('utf-8')
一樣的結(jié)果。callable()
:判斷一個(gè)對(duì)象是否可調(diào)用,學(xué)完P(guān)ython面向?qū)ο缶幊叹涂梢岳斫饬恕?/p>chr()
:返回一個(gè)數(shù)字對(duì)應(yīng)的ascii字符,比如chr(90)
就返回ascii碼表里90號(hào)對(duì)應(yīng)的字符'Z'
classmethod()
:也是面向?qū)ο缶幊淌褂?,后面用到再說(shuō)。complie()
:Python解釋器自己用的,忽略complex()
:求復(fù)數(shù),比如complex(10, 9)
返回的是10+9j
,做工程的可能會(huì)用到,我們普通程序員一般不會(huì)用到。copyright()
:沒(méi)用credits()
:Python感謝開(kāi)發(fā)者的delattr()
:面向?qū)ο笥?,后面再說(shuō)dict()
:生成一個(gè)空字典dir()
:返回對(duì)象的可調(diào)用屬性,比如dir([])
,他就會(huì)返回list
的一些可調(diào)用的方法,啥append()
、sort()
等等divmod()
:返回出發(fā)的商和余數(shù),比如divmod(4, 2)
返回(2, 0)
,以元組形式返回enumerate()
:返回列表的索引和元素,常用,不過(guò)也可以通過(guò)循環(huán)和index()
的搭配來(lái)實(shí)現(xiàn)。?a = ['zm', 'ghd']
?for i in enumerate(a):
? ? ?print(f'enumerate 0:{i[0]} | enumerate 1:{i[1]}')
?# 輸出:
?# enumerate 0:0 | enumerate 1:zm
?# enumerate 0:1 | enumerate 1:ghdeval()
:可以把字符串形式的list、dict、set、tuple在轉(zhuǎn)換成其原有的數(shù)據(jù)類(lèi)型。這個(gè)其實(shí)是Python解釋器用的,只是我們也用的到。exec()
:把字符串格式的代碼,進(jìn)行解義并執(zhí)行,比如exec("print('hello world')")
,代碼就會(huì)輸出hello world
。exit()
:退出程序filter()
:對(duì)list、dict、set、tuple等可迭代對(duì)象進(jìn)行過(guò)濾,可以搭配匿名函數(shù)使用,比如過(guò)濾出所有大于10的值:filter(lambda x: x>10, [1, 2, 24, 4, 11, 67, 7])
,然后for循環(huán)輸出即可。float()
:轉(zhuǎn)成浮點(diǎn)數(shù)format()
:格式化輸出,很少用,用個(gè)人喜歡用f''
frozenset()
:把一個(gè)集合變成不可修改的集合。getattr()
:面向?qū)ο髸r(shí)使用,后面再說(shuō)。globals()
:返回所有的全局變量。hasattr()
:面向?qū)ο笫褂茫竺嬖僬f(shuō)。hash()
:傳入任意參數(shù),生成并返回哈希值。help()
:幫助,進(jìn)去之后可以查看各種東西。hex()
:返回10進(jìn)制數(shù)的16進(jìn)制表示格式。id()
:返回傳入數(shù)據(jù)的內(nèi)存地址。input()
:獲取輸入的數(shù)據(jù)。int()
:轉(zhuǎn)成整型。isinstance()
:判斷數(shù)據(jù)是什么類(lèi)型,比如isinstance([1, 2], list)
就會(huì)返回True,isinstance({}, set)
就是False。issubclass()
:面向?qū)ο髸r(shí)用到,后面再說(shuō)。iter()
:轉(zhuǎn)成可迭代對(duì)象。len()
:返回傳入?yún)?shù)的長(zhǎng)度。list()
:轉(zhuǎn)成列表。locals()
:返回當(dāng)前位置的局部變量。map()
:前面用過(guò),和filter()
原理相似。max()
:返回傳入?yún)?shù)中的最大值,字典、列表等都可以,不過(guò)必須全是數(shù)字或者全是字符串(一般是)。memoryview()
:一般人不用,大神用的,忽略。min()
:返回傳入?yún)?shù)中的最小值。next()
:生成器會(huì)用到,下面馬上會(huì)說(shuō)。object()
:面向?qū)ο髸r(shí)用,后面再說(shuō)。oct()
:將10進(jìn)制數(shù)返回8進(jìn)制。open()
:文件操作,前面細(xì)說(shuō)過(guò),不講了。ord()
:返回ascii字符對(duì)應(yīng)的10進(jìn)制數(shù)和chr()
正好相反。print()
:輸出。property()
:面向?qū)ο髸r(shí)用,后面再說(shuō)。quit()
:退出。range()
:數(shù)字列表,前面說(shuō)過(guò),不再贅述。repr()
:沒(méi)什么用,忽略reversed()
:反轉(zhuǎn)列表,和列表的反轉(zhuǎn)方法一樣。round()
:把小數(shù)四舍五入成整數(shù),注意4.5會(huì)變成4,只有4.51才是5,要比5大一丁點(diǎn)才會(huì)進(jìn)位,單純的5不行,和日常生活中不一樣,需要注意一下。round(10.15, 1)
返回10.2,保留一位小數(shù),不填第二個(gè)參數(shù)的話(huà),就默認(rèn)是整數(shù)比如round(10.15)
返回10。set()
:轉(zhuǎn)成集合。setattr()
:面向?qū)ο髸r(shí)用,后面再說(shuō)。slice()
:沒(méi)用。sorted()
:列表的sort()
一樣效果staticmethod()
:面向?qū)ο髸r(shí)用,后面再說(shuō)。str()
:轉(zhuǎn)成字符。sum()
:求和,必須都是數(shù)字super()
:面向?qū)ο髸r(shí)用,后面再說(shuō)。tuple()
:轉(zhuǎn)成元組type()
:返回?cái)?shù)據(jù)的類(lèi)型vars()
:面向?qū)ο髸r(shí)用,后面再說(shuō)。zip()
:把多個(gè)列表拼成一個(gè),和傳統(tǒng)的合并不太一樣。。。看看下面的例子就懂了?a = [1, 4, 9, 10, 11]
?b = ['a', 'b', 'c']
?print(list(zip(a, b)))
?
?# 輸出:[(1, 'a'), (4, 'b'), (9, 'c')]有些東西會(huì)丟棄,以最短的為標(biāo)準(zhǔn),新列表里面的元素是以元組為單位的,兩個(gè)列表拼一起就是二元組,多個(gè)就是多元組。
3.2.8 NameSpace和閉包現(xiàn)象
名稱(chēng)空間NameSpace
python里我們寫(xiě)x=1
,1我們知道存在哪,x就是一個(gè)指針,指向1的地址,這個(gè)我們前面都解釋過(guò),但是x和1的對(duì)應(yīng)關(guān)系存儲(chǔ)在哪呢?也就是x存在哪呢?那就是存在名稱(chēng)空間里面。
python有很多個(gè)名稱(chēng)空間,這些名稱(chēng)空間互不干擾,不同空間內(nèi)相同名字的變量也沒(méi)有任何聯(lián)系。
名稱(chēng)空間有4類(lèi):LEGB
locals
:函數(shù)內(nèi)部的名字空間,一般有函數(shù)的局部變量以及形式參數(shù)。encolsing function
:在嵌套函數(shù)中的外部函數(shù)的名字空間。若fun2嵌套在fun1里面,fun1的名字空間就是enclosing function
。globals
:當(dāng)前的模塊空間,模塊就是一些py文件,可以說(shuō)全局變量。__Builtins__
:內(nèi)置模塊空間,放內(nèi)置變量或內(nèi)置函數(shù)的名字空間,print(dir(__builtins__))
可以查看包含的值,我們Python拋出的一些錯(cuò)誤,比如ValueError
等也放在這里。
這個(gè)就可以解釋?zhuān)瑸槭裁纯梢酝瑫r(shí)存在同名的全局變量和局部變量,因?yàn)樗膫€(gè)名稱(chēng)空間互不干擾,locals
和globals
兩個(gè)里面同名的變量不會(huì)產(chǎn)生錯(cuò)誤。
不同變量的作用域也就是由這個(gè)變量所在的名稱(chēng)空間決定的。
作用域即范圍:
全局范圍:全局有效,可以當(dāng)是一直有的
局部范圍:局部有效,可以當(dāng)是臨時(shí)有的
作用域的查找順序優(yōu)先從自己的名稱(chēng)空間找,順序是按L->E->G->B,還找不到,那就拋出NameError
的異常,表示變量名稱(chēng)不存在。這里也可以解釋前面函數(shù)修改變量產(chǎn)生的一些現(xiàn)象。注意哈,如果是多層嵌套的話(huà),E還有好幾層的,就由所在內(nèi)部函數(shù)一層一層往上找就好。理解這些順序就好了,這樣之后遇到自己代碼因?yàn)檫@個(gè)報(bào)錯(cuò)的話(huà),就可以知道怎么找出錯(cuò)誤了。
閉包現(xiàn)象
什么是閉包
?def outer():
? ? ?name = 'I am zm'
? ? ?
? ? ?def inner():
? ? ? ? ?print(name)
? ? ?
? ? ?return inner
?
?print(outer())
?func = outer()
?func() # 輸出I am zm
這里我們輸出就會(huì)發(fā)現(xiàn),outer()
返回的是inner()
的函數(shù)地址,所以我們就可以在函數(shù)外部調(diào)用outer()
的內(nèi)部函數(shù)inner()
。
不過(guò),這里也就引出了一個(gè)問(wèn)題。我們前面說(shuō)過(guò),函數(shù)執(zhí)行結(jié)束過(guò)后他執(zhí)行占用的內(nèi)存就會(huì)被全部釋放,但是這里的現(xiàn)象就是outer()
的內(nèi)部函數(shù)inner()
還是可以繼續(xù)用的,并沒(méi)有被釋放,這個(gè)現(xiàn)象就叫閉包現(xiàn)象,官方也有定義“這個(gè)黏黏糊糊的現(xiàn)象就是閉包”。
閉包的意義
返回的函數(shù)對(duì)象,不僅僅是一個(gè)函數(shù)對(duì)象,在該函數(shù)外還包裹了一層作用域,這使得,該函數(shù)無(wú)論在何處調(diào)用,優(yōu)先使用自己外層包裹的作用域。簡(jiǎn)單說(shuō),就是改變了該函數(shù)的作用域。
閉包在下面的裝飾器就會(huì)用到,一些企業(yè)面試也可能會(huì)問(wèn)這個(gè)問(wèn)題。
3.3 函數(shù)進(jìn)階
3.3.1 裝飾器
要說(shuō)清楚這個(gè)裝飾器,我們就先假設(shè)一個(gè)場(chǎng)景?,F(xiàn)在你是一家視頻網(wǎng)站的后端開(kāi)發(fā)工程師,網(wǎng)站分別有一下幾個(gè)板塊(簡(jiǎn)單的模擬)。
?def home():
? ? ?print('----首頁(yè)----')
?def beijing():
? ? ?print('----北京專(zhuān)區(qū)----')
?def henan():
? ? ?print('----河南專(zhuān)區(qū)----')
?def anhui():
? ? ?print('----安徽專(zhuān)區(qū)----')
視頻上線(xiàn)初期,為了吸引用戶(hù),全部免費(fèi)觀看,之后因?yàn)閷拵зM(fèi)用公司承受不了,要對(duì)比較受歡迎的幾個(gè)板塊收費(fèi),其中包括北京和河南。那我們得先讓其用戶(hù)認(rèn)證,然后判斷是否是VIP會(huì)員就可以了。然后我們寫(xiě)了相應(yīng)的函數(shù)。
?account = {
? ? ?"is_authenticated": False, # 用戶(hù)登錄成功就變成True
? ? ?"username": "zm", # 假裝是DataBase的用戶(hù)信息
? ? ?"password": "abc123", # 假裝是DataBase的密碼信息
?}
?
?def login():
? ? ?if account["is_authenticated"]:
? ? ? ? ?username = input('user:')
? ? ? ? ?password = input('password:')
? ? ? ? ?if username == account["username"] and password == account["password"]:
? ? ? ? ? ? ?print('welcome login ...')
? ? ? ? ? ? ?account["is_authenticated"] = True
? ? ? ? ?else:
? ? ? ? ? ? ?print('wrong username or password!')
? ? ?else:
? ? ? ? ?print('已登錄,通過(guò)驗(yàn)證...')
?
?def home():
? ? ?print('----首頁(yè)----')
?def beijing():
? ? ?login()
? ? ?print('----北京專(zhuān)區(qū)----')
?def henan():
? ? ?login()
? ? ?print('----河南專(zhuān)區(qū)----')
?def anhui():
? ? ?login()
? ? ?print('----安徽專(zhuān)區(qū)----')
這樣我們哪個(gè)界面需要登錄認(rèn)證我們加上login()
就可以了。但是,這已經(jīng)違反軟件開(kāi)發(fā)中的一個(gè)“開(kāi)放-封閉”原則,簡(jiǎn)單來(lái)說(shuō),就是已經(jīng)實(shí)現(xiàn)功能的代碼不應(yīng)該被修改(封閉),但可以被擴(kuò)展(開(kāi)放)。
那我們就想到了高階函數(shù),把函數(shù)當(dāng)做參數(shù)傳入。哪個(gè)專(zhuān)區(qū)需要認(rèn)證,我們就把那個(gè)專(zhuān)區(qū)的函數(shù)名當(dāng)做參數(shù)傳入login()
,大概代碼如下。
?account = {
? ? ?"is_authenticated": False,
? ? ?"username": "zm",
? ? ?"password": "abc123",
?}
?
?def login(func):
? ? ?if account["is_authenticated"]:
? ? ? ? ?username = input('user:')
? ? ? ? ?password = input('password:')
? ? ? ? ?if username == account["username"] and password == account["password"]:
? ? ? ? ? ? ?print('welcome login ...')
? ? ? ? ? ? ?account["is_authenticated"] = True
? ? ? ? ? ? ?func() ?# 改了這里
? ? ? ? ?else:
? ? ? ? ? ? ?print('wrong username or password!')
? ? ?else:
? ? ? ? ?func() ?# 改了這里
?
?def home():
? ? ?print('----首頁(yè)----')
?def beijing():
? ? ?print('----北京專(zhuān)區(qū)----')
?def henan():
? ? ?print('----河南專(zhuān)區(qū)----')
?def anhui():
? ? ?print('----安徽專(zhuān)區(qū)----')
? ? ?
?login(anhui)
?login(henan)
那我們就完成了任務(wù)了。但是,這樣的代碼被老開(kāi)發(fā)者來(lái)說(shuō),是要被唾棄的。因?yàn)槟愀淖兞苏{(diào)用方式,現(xiàn)在所有專(zhuān)區(qū)的模塊必須調(diào)用login()
才可以加上認(rèn)證這個(gè)功能,而企業(yè)開(kāi)發(fā)中其他模塊的功能代碼肯定對(duì)這些anhui()
、henan()
這些函數(shù)有很多的調(diào)用,現(xiàn)在他們必須全加上login()
才行,想想這個(gè)工程有多大,肯定會(huì)被罵的。
我們?cè)倏聪履涿瘮?shù)和普通函數(shù):
?def plus(n):
? ? ?return n+1
?plus_2 = lambda x: x+1
?calc = plus_2
?print(calc(2)) # 輸出3
我們發(fā)現(xiàn)我們可以給函數(shù)另起名字,而且同樣可以調(diào)用函數(shù)功能那我們應(yīng)用到我們的代碼中,演變成anhui = login(anhui)
,然后我們下面繼續(xù)調(diào)用anhui()
。
但是運(yùn)行過(guò)后,我們又會(huì)發(fā)現(xiàn)anhui()
報(bào)錯(cuò),我們找了下原因,就是login(anhui)
的時(shí)候已經(jīng)調(diào)用了anhui()
,然后anhui()
返回的是None
,所以最后anhui = login(anhui)
時(shí),anhui
獲得的是None
。我們可以將最后的anhui()
改成print(anhui)
看看其輸出。
那我們的問(wèn)題就變成了怎么延緩login(func)
的調(diào)用呢?
我們想起了前面所提到的一個(gè)黏黏糊糊的現(xiàn)象——閉包。我們可以將login()
函數(shù)改一下,改成一個(gè)嵌套結(jié)構(gòu),然后return
內(nèi)部函數(shù)名,搞成閉包的現(xiàn)象。
?account = {
? ? ?"is_authenticated": False,
? ? ?"username": "zm",
? ? ?"password": "abc123",
?}
?
?def login(func):
? ? ?def inner():
? ? ? ? ?if account["is_authenticated"]:
? ? ? ? ?username = input('user:')
? ? ? ? ?password = input('password:')
? ? ? ? ?if username == account["username"] and password == account["password"]:
? ? ? ? ? ? ?print('welcome login ...')
? ? ? ? ? ? ?account["is_authenticated"] = True
? ? ? ? ? ? ?func()
? ? ? ? ?else:
? ? ? ? ? ? ?print('wrong username or password!')
? ? ?else:
? ? ? ? ?func()
? return inner
?
?def home():
? ? ?print('----首頁(yè)----')
?def beijing():
? ? ?print('----北京專(zhuān)區(qū)----')
?def henan():
? ? ?print('----河南專(zhuān)區(qū)----')
?def anhui():
? ? ?print('----安徽專(zhuān)區(qū)----')
? ? ?
?anhui = login(anhui) # 這里anhui拿到的就是inner函數(shù)
?henan = login(henan) # henan拿到的也是inner函數(shù)
?
?print(anhui) # 輸出的是inner的函數(shù)地址
?
?anhui() # 成功進(jìn)入認(rèn)證
?henan() # 第二次無(wú)需認(rèn)證,成功
這樣我們其他模塊調(diào)用anhui()
時(shí),不需要加任何東西,正常寫(xiě)anhui()
調(diào)用,就可以實(shí)現(xiàn)驗(yàn)證的功能。
那我們現(xiàn)在因?yàn)?code>anhui()的一些視頻點(diǎn)擊量很大,我們就要加上vip的等級(jí),從原來(lái)的函數(shù)修改了一下,增加了一個(gè)參數(shù)。
?def anhui(vip_level):
? ? ?if vip_level > 3:
? ? ? ? ?print('全部解鎖!')
? ? ?else:
? ? ? ? ?print('----安徽專(zhuān)區(qū)----')
然后,如果我們繼續(xù)用anhui = login(anhui(4))
的話(huà),我們會(huì)發(fā)現(xiàn)少參數(shù),python會(huì)報(bào)錯(cuò)的,因?yàn)榉祷氐?code>inner()是不帶參的,那anhui
拿到的就是不帶參的anhui()
而不是一開(kāi)始的anhui(vip_level)
了。
那很簡(jiǎn)單,我們修改一下login()
不就好了,inner()
改成inner(arg)
,inner()
返回的func()
改成func(arg)
即可。那我們?cè)俅芜\(yùn)行就會(huì)發(fā)現(xiàn),anhui()
雖然不報(bào)錯(cuò)了,但是henan()
報(bào)錯(cuò)了,因?yàn)樗粋鲄?,不需要判別vip等級(jí)。
那怎么辦?其實(shí)也很簡(jiǎn)單,我們可以使用非固定參數(shù),把上面說(shuō)到的arg
換成*args, **kwargs
:
?def login(func):
? ? ?def inner(*args, **kwargs): # 這里
? ? ? ? ?if account["is_authenticated"]:
? ? ? ? ? ? ?username = input('user:')
? ? ? ? ? ? ?password = input('password:')
? ? ? ? ? ? ?if username == account["username"] and password == account["password"]:
? ? ? ? ? ? ? ? ?print('welcome login ...')
? ? ? ? ? ? ? ? ?account["is_authenticated"] = True
? ? ? ? ? ? ? ? ?func(*args, **kwargs) # 這里
? ? ? ? ? ? ?else:
? ? ? ? ? ? ? ? ?print('wrong username or password!')
? ? ? ? ?else:
? ? ? ? ? ? ?func(*args, **kwargs) # 這里
? return inner
好了,這樣我們anhui(4)
就不會(huì)報(bào)錯(cuò)了,henan()
也不會(huì)報(bào)錯(cuò)。
?account = {
? ? ?"is_authenticated": False,
? ? ?"username": "zm",
? ? ?"password": "abc123",
?}
?
?def login(func):
? ? ?def inner(*args, **kwargs):
? ? ? ? ?if account["is_authenticated"]:
? ? ? ? ? ? ?username = input('user:')
? ? ? ? ? ? ?password = input('password:')
? ? ? ? ? ? ?if username == account["username"] and password == account["password"]:
? ? ? ? ? ? ? ? ?print('welcome login ...')
? ? ? ? ? ? ? ? ?account["is_authenticated"] = True
? ? ? ? ? ? ? ? ?func(*args, **kwargs)
? ? ? ? ? ? ?else:
? ? ? ? ? ? ? ? ?print('wrong username or password!')
? ? ? ? ?else:
? ? ? ? ? ? ?func(*args, **kwargs)
? return inner
?
?def home():
? ? ?print('----首頁(yè)----')
?
?def beijing():
? ? ?print('----北京專(zhuān)區(qū)----')
?
?@login # 用到裝飾器的函數(shù)就加一個(gè)@裝飾器名,實(shí)現(xiàn)覆蓋,方便后期維護(hù)
?def henan():
? ? ?print('----河南專(zhuān)區(qū)----')
?
?@login
?def anhui(vip_level):
? ? ?if vip_level > 3:
? ? ? ? ?print('全部解鎖!')
? ? ?else:
? ? ? ? ?print('----安徽專(zhuān)區(qū)----')
?
?# 寫(xiě)了@login后,python自動(dòng)就幫你執(zhí)行了anhui = login(anhui),不用自己寫(xiě)了,直接調(diào)用即可
?# anhui = login(anhui) # 這里anhui拿到的就是inner函數(shù)
?# henan = login(henan) # henan拿到的也是inner函數(shù)
?
?anhui(4) # 沒(méi)報(bào)錯(cuò)
?henan() # 沒(méi)報(bào)錯(cuò)
那上面的代碼就是最終的裝飾器了,在日后的開(kāi)發(fā)過(guò)程中,是一個(gè)很重要的知識(shí)點(diǎn),會(huì)經(jīng)常使用。裝飾器也稱(chēng)作是語(yǔ)法糖,裝飾器也可以嵌套,比如anhui()
在被login()
裝飾的基礎(chǔ)上,我們?cè)賹?xiě)一個(gè)pay_money()
再裝飾一下,我們就要寫(xiě)成
?@pay_money
?@login
?def anhui():
? ? ?.......
好了,至此裝飾器就結(jié)束了。
3.3.2 列表生成式
先有個(gè)需求,給列表每個(gè)元素加一,a = [0, 1, 2, 3, 4]
?# 生成一個(gè)新列表 ? 浪費(fèi)空間
?a = [0, 1, 2, 3, 4]
?b = []
?for i in a:
? b.append(i+1)
?a = b?# 通過(guò)下標(biāo),修改value ?毫無(wú)新意
?a = [0, 1, 2, 3, 4]
?for i, v in enumerate(a):
? ? ?a[i] = v+1
?print(a)?# 匿名函數(shù)
?a = [0, 1, 2, 3, 4]
?a = list(map(lambda x: x+1, a))
??# 列表生成式
?a = [0, 1, 2, 3, 4]
?a = [i+1 for i in a] # 這就是列表生成式,有時(shí)候會(huì)用
?# 列表生成式也相當(dāng)于創(chuàng)建了一個(gè)新列表,也占空間
3.3.3 生成器
生成器
生成器,又稱(chēng)generator
現(xiàn)在有如下代碼:
?for i in range(100000):
? ? ?print(i)
? ? ?if i > 100:
? ? ? ? ?break
我們可以看出上述代碼非常浪費(fèi)空間,i=101
時(shí)break
,range()
生成的十萬(wàn)個(gè)元素的列表除了用了101個(gè),其他全浪費(fèi)了。(while
循環(huán)實(shí)現(xiàn)即可,while
循環(huán)原理類(lèi)似生成器)(這里的range()
在python3優(yōu)化成了生成器,就不會(huì)浪費(fèi)空間了)
那我們實(shí)現(xiàn)一個(gè)一邊循環(huán),一邊計(jì)算下面一個(gè)元素,這種機(jī)制就叫生成器。
生成器優(yōu)化循環(huán)效率
這個(gè)就是一個(gè)生成器g = (x*x for x in range(10))
,它只會(huì)保存這個(gè)算法,就生成了一個(gè)生成器的對(duì)象。我們?nèi)绻∠乱粋€(gè)元素,就調(diào)用Python的內(nèi)置方法next()
,next(g)
第一次返回0,然后我們可以一直next()
,直到后面沒(méi)有元素了,就會(huì)拋出StopIteration
異常。這種計(jì)算我們叫做惰性運(yùn)算。
斐波拉契數(shù)列
?# 輸出前100項(xiàng)Fibonacci數(shù)列 斐波拉契數(shù)列
?a, b = 0, 1
?n = 0
?while n < 100:
? ? ?n = a + b
? ? ?a = b
? ? ?b = n
? ? ?print(n)
函數(shù)生成器
上面的斐波拉契數(shù)列我們應(yīng)該可以了解,我們不能用一個(gè)普通的生成器來(lái)實(shí)現(xiàn)斐波拉契數(shù)列。那我們就引入一個(gè)函數(shù)生成器的概念。
我們先將上面的while循環(huán)改成一個(gè)函數(shù):
?def fib(n):
? ? ?a, b = 0, 1
? ? ?i = 0
? ? ?count = 0
? ? ?while i < n:
? ? ? ? ?i = a + b
? ? ? ? ?a = b
? ? ? ? ?b = i
? ? ? ? ?print(i)
? ? ? ? ?count += 1
然后我們將print(i)
(或者return i
)改成yield i
,函數(shù)就變成了函數(shù)生成器。
?def fib(n):
? ? ?a, b = 0, 1
? ? ?i = 0
? ? ?count = 0
? ? ?while i < n:
? ? ? ? ?i = a + b
? ? ? ? ?a = b
? ? ? ? ?b = i
? ? ? ? ?yield i ?# 暫停,next()才會(huì)繼續(xù)循環(huán)
? ? ? ? ?count += 1
我們可以輸出查看fib()
函數(shù)返回的數(shù)據(jù)類(lèi)型,我們發(fā)現(xiàn)是generator
。然后我們就可以next(fib)
(或者fib.__next__()
,一樣的效果)
函數(shù)生成器我們可以實(shí)現(xiàn)先執(zhí)行用幾次函數(shù),然后干其他的事,接著再執(zhí)行函數(shù),函數(shù)生成式可以使函數(shù)占用 的內(nèi)存一直不釋放。
生成器實(shí)現(xiàn)并發(fā)編程
好了,上面我們說(shuō)了函數(shù)生成器如何向外傳參數(shù)(yield n
),那我們可不可以向里傳入?yún)?shù)呢?
答案是可以的,
?def g_test():
? while True:
? ? ? ? ?n = yield
? ? ? ? ?print('receive from outside:', n)
?
?g = g_test()
?g.__next__() # 第一次next會(huì)調(diào)用生成器,同時(shí)會(huì)發(fā)送None到y(tǒng)ield
?
?for i in range(10):
? ? ?g.send(i) # 調(diào)用生成器,同時(shí)發(fā)送i到y(tǒng)ield
生成器應(yīng)用非常廣,Python中循環(huán)、文件操作等等底層都是用生成器來(lái)實(shí)現(xiàn)的。
那我們現(xiàn)在來(lái)用函數(shù)生成器實(shí)現(xiàn)單線(xiàn)程下的多并發(fā)效果(微觀的串行,宏觀的并行)。
?# 吃包子
?# 消費(fèi)者吃包子:p1, p2, p3
?# 生產(chǎn)者做包子:
?def consumer(name):
? ? ?print(name + '準(zhǔn)備吃包子啦')
? ? ?while True:
? ? ? ? ?baozi = yield ?# 接收外面的包子
? ? ? ? ?print(name+'收到包子, '+'包子編號(hào)為'baozi)
?
?p1 = consumer("P1")
?p2 = consumer("P2")
?p3 = consumer("P3")
?
?p1.__next__()
?p2.__next__()
?p3.__next__()
?
?for i in range(10):
? ? ?print('生成了第%s屜包子-----------' % i)
? ? ?# 一屜包子三個(gè)
? ? ?p1.send(i)
? ? ?p2.send(i)
? ? ?p3.send(i)
? ? ?
?# 我們就實(shí)現(xiàn)了單線(xiàn)程的并發(fā)效果,三個(gè)人同時(shí)吃包子
3.3.4 迭代器
for循環(huán)的數(shù)據(jù)類(lèi)型有以下幾種:
集合數(shù)據(jù)類(lèi)型,eg:list、tuple、dict、set、str等
generator,包括生成器和帶yield的函數(shù)生成器
這些可以被for循環(huán)的,就叫可迭代對(duì)象。
可迭代對(duì)象我們可以用isinstance(X, Iterable)
來(lái)判斷(需要導(dǎo)入collections
庫(kù))
迭代器我們可以用isinstance(X, Iterator)
迭代器可以next()
,可迭代對(duì)象只是可以被循環(huán)。
iter()
將參數(shù)轉(zhuǎn)換成迭代器,使其可以被next()
。
迭代器近似就是生成器,可以這樣理解,面試一般不會(huì)問(wèn),區(qū)別我們?cè)诰毩?xí)題11中解釋了。
3.4 練習(xí)題
參考http://book.luffycity.com/python-book/315-lian-xi-989826-zuo-ye.html
自己實(shí)現(xiàn)的代碼:
?# 第1題
?def sum_num(*args):
? ? ?if args:
? ? ? ? ?return sum(args)
? ? ?else:
? ? ? ? ?return 0
?
?
?print(sum_num(1, 2, 3.5))
?print(sum_num())
?print(sum_num(1.2, 2.1, 3.5))
?
?# 第2題
?import os
?
?def replace_file(filename, old_str, new_str):
? ? ?with open(filename, 'r', encoding='utf-8') as f:
? ? ? ? ?with open('new.txt', 'w', encoding='utf-8') as new_f:
? ? ? ? ? ? ?for line in f.readlines():
? ? ? ? ? ? ? ? ?if old_str in line:
? ? ? ? ? ? ? ? ? ? ?line = line.replace(old_str, new_str)
? ? ? ? ? ? ? ? ? ? ?new_f.write(line)
? ? ? ? ? ? ? ? ?else:
? ? ? ? ? ? ? ? ? ? ?new_f.write(line)
?
? ? ?os.replace('new.txt', filename)
?
?replace_file('test.txt', 'wzw', 'py')
?# 文件為同目錄下的test.txt,內(nèi)容見(jiàn)下面的附錄
?
?# 第3題
?def all_true(data):
? ? ?if isinstance(data, list):
? ? ? ? ?if all(data):
? ? ? ? ? ? ?print('該列表中的元素全不為空')
? ? ? ? ?else:
? ? ? ? ? ? ?print('該列表中存在空元素')
? ? ?elif isinstance(data, tuple):
? ? ? ? ?if all(data):
? ? ? ? ? ? ?print('該元組中的元素全不為空')
? ? ? ? ?else:
? ? ? ? ? ? ?print('該元組中存在空元素')
? ? ?elif isinstance(data, str):
? ? ? ? ?if all(data):
? ? ? ? ? ? ?print('該字符串中的元素全不為空')
? ? ? ? ?else:
? ? ? ? ? ? ?print('該字符串中存在空元素')
? ? ?else:
? ? ? ? ?print('請(qǐng)傳入列表、元組或字符串類(lèi)型的數(shù)據(jù)')
?
?all_true([1, 2, 0])
?all_true([1, 2, 3])
?all_true([1, [], 2])
?all_true([1, {}, 2])
?all_true((1, 2, 3))
?all_true((1, 2, []))
?all_true('012')
?all_true('01 ')
?all_true('')
?
?# 第4題
?def cut_item(data):
? ? ?"""
? ? ?將列表、字符、字典所有元素長(zhǎng)度強(qiáng)制小于等于2
? ? ?:param data: 要處理的數(shù)據(jù)
? ? ?:return: 處理完的數(shù)據(jù)
? ? ?"""
? ? ?if isinstance(data, str):
? ? ? ? ?cut_two_str(data)
? ? ?elif isinstance(data, list):
? ? ? ? ?data = cut_two_list(data)
? ? ?elif isinstance(data, dict):
? ? ? ? ?data = cut_two_dict(data)
?
? ? ?return data
?
?def cut_two_list(cut_list):
? ? ?"""
? ? ?將列表的所有元素長(zhǎng)度強(qiáng)制小于等于2,包括嵌套的字典和列表結(jié)構(gòu)
? ? ?:param cut_list: 要處理的列表
? ? ?:return: 處理完的列表
? ? ?"""
? ? ?for i, v in enumerate(cut_list):
? ? ? ? ?if isinstance(v, list):
? ? ? ? ? ? ?cut_two_list(v)
? ? ? ? ?elif isinstance(v, dict):
? ? ? ? ? ? ?cut_two_dict(v)
? ? ? ? ?elif isinstance(v, str):
? ? ? ? ? ? ?if len(v) > 2:
? ? ? ? ? ? ? ? ?cut_list[i] = v[:2]
?
? ? ?return cut_list
?
?def cut_two_dict(cut_dict):
? ? ?"""
? ? ?將字典的所有元素長(zhǎng)度強(qiáng)制小于等于2,包括嵌套的列表和字典結(jié)構(gòu)
? ? ?:param cut_dict: 要處理的字典
? ? ?:return: 處理完的字典
? ? ?"""
? ? ?for i, v in cut_dict.items():
? ? ? ? ?if isinstance(v, list):
? ? ? ? ? ? ?cut_two_list(v)
? ? ? ? ?elif isinstance(v, dict):
? ? ? ? ? ? ?cut_two_dict(v)
? ? ? ? ?elif isinstance(v, str):
? ? ? ? ? ? ?if len(v) > 2:
? ? ? ? ? ? ? ? ?cut_dict[i] = v[:2]
? ? ?return cut_dict
?
?def cut_two_str(cut_str):
? ? ?"""
? ? ?將字符的長(zhǎng)度強(qiáng)制小于等于2
? ? ?:param cut_str: 要處理的字符
? ? ?:return: 處理完的字符
? ? ?"""
? ? ?if len(cut_str) > 2:
? ? ? ? ?cut_str = cut_str[:2]
? ? ?return cut_str
?
?print(cut_item('zmw'))
?print(cut_item('zm'))
?print(cut_item(['wzw', 'zzw', 'zm', 'ghd', 'py']))
?print(cut_item(['wzw', 'zzw', 'zm', ['ghd', 'py']]))
?print(cut_item([{'1': 'wzw', '2': 'zzw', '3': 'zm'}, ['ghd', 'py']]))
?print(cut_item({'1': 'wzw', '2': 'zzw', '3': 'zm', '4': 'ghd', '5': 'py'}))
?print(cut_item({'1': 'wzw', '2': 'zzw', '3': 'zm', '4': ['ghd', 'py']}))
?
?# 第5題
?閉包就是一種保留內(nèi)部函數(shù)的一種現(xiàn)象,可以使外部函數(shù)結(jié)束運(yùn)行的情況下,還可以繼續(xù)調(diào)用內(nèi)部函數(shù)。官方文檔稱(chēng)之為黏黏糊糊的現(xiàn)象。
?
?# 第6題
?def make_card():
? ? ?card = []
? ? ?before_name = ['?', '?', '?', '?']
? ? ?after_name = [i for i in range(1, 11)]
? ? ?special_after = ['J', 'Q', 'K', 'A']
? ? ?special_name = ['小王', '大王']
?
? ? ?for before in before_name:
? ? ? ? ?for after in after_name:
? ? ? ? ? ? ?card.append((before, str(after)))
? ? ? ? ?for after in special_after:
? ? ? ? ? ? ?card.append((before, after))
? ? ?card.append(special_name[0])
? ? ?card.append(special_name[1])
? ? ?return card
?
?print(make_card())
?
?# 第7題
?def min_max(*args):
? ? ?return_dict = {'max': max(args), 'min': min(args)}
? ? ?return return_dict
?
?
?print(min_max(1, 3, 9, 4, 5, 19.3, 13.1, 0, -3))
?
?# 第8題
?from math import pi
?
?def area(*args):
? ? ?def calc_rectangle_area(length, width,):
? ? ? ? ?return length * width
?
? ? ?def calc_square_area(length,):
? ? ? ? ?return length ** 2
?
? ? ?def calc_round_area(radius,):
? ? ? ? ?return pi * (radius ** 2)
?
? ? ?if args[0] == '圓形':
? ? ? ? ?try:
? ? ? ? ? ? ?data = calc_round_area(args[1])
? ? ? ? ?except TypeError:
? ? ? ? ? ? ?print('請(qǐng)輸入正確的格式,圓形計(jì)算格式為 ("圓形", 半徑)')
? ? ?elif args[0] == '正方形':
? ? ? ? ?try:
? ? ? ? ? ? ?data = calc_square_area(args[1])
? ? ? ? ?except TypeError:
? ? ? ? ? ? ?print('請(qǐng)輸入正確的格式,正方形計(jì)算格式為 ("正方形", 邊長(zhǎng))')
? ? ?elif args[0] == '長(zhǎng)方形':
? ? ? ? ?try:
? ? ? ? ? ? ?data = calc_rectangle_area(args[1], args[2])
? ? ? ? ?except TypeError:
? ? ? ? ? ? ?print('請(qǐng)輸入正確的格式,長(zhǎng)方形計(jì)算格式為 ("長(zhǎng)方形", 邊長(zhǎng))')
? ? ?return data
?
?print(area('圓形', 1))
?print(area('正方形', 1.5))
?print(area('長(zhǎng)方形', 2.3, 10))
?
?# 第9題
?def factorial(n):
? ? ?temp = 1
? ? ?for i in [x for x in range(1, n+1)]:
? ? ? ? ?temp = temp * i
? ? ?return temp
?
?print(factorial(3))
?print(factorial(4))
?print(factorial(10))
?
?# 第10題
?# 本代碼所需的test.txt位于同目錄下,內(nèi)容見(jiàn)下面附錄
?info = {
? ? ?'is_authentication': False,
? ? ?'user_info': [],
?}
?
?def data_init(filename):
? ? ?with open(filename, 'r', encoding='utf-8') as f:
? ? ? ? ?for line in f.readlines():
? ? ? ? ? ? ?info['user_info'].append(line.strip().split(','))
?
?def login(func):
?
? ? ?def inner(*args, **kwargs):
? ? ? ? ?counts = [i[0] for i in info['user_info']]
? ? ? ? ?if not info['is_authentication']:
? ? ? ? ? ? ?user = input('please input your username: ')
? ? ? ? ? ? ?pwd = input('please input your password: ')
? ? ? ? ? ? ?if user in counts:
? ? ? ? ? ? ? ? ?if [user, pwd] in info['user_info']:
? ? ? ? ? ? ? ? ? ? ?info['is_authentication'] = True
? ? ? ? ? ? ? ? ? ? ?func(*args, **kwargs)
? ? ? ? ? ? ? ? ?else:
? ? ? ? ? ? ? ? ? ? ?print('密碼錯(cuò)誤!')
? ? ? ? ? ? ?else:
? ? ? ? ? ? ? ? ?print('用戶(hù)名不存在!')
? ? ? ? ?else:
? ? ? ? ? ? ?func()
?
? ? ?return inner
?
?@login
?def ghd():
? ? ?print('----- 歡迎進(jìn)入該模塊 ----')
?
?@login
?def zm():
? ? ?print('----- 歡迎使用該功能 ----')
?
?data_init('test.txt')
?ghd()
?zm()
?
?# 第11題
?迭代器與生成器的區(qū)別:
?(1)生成器:
?1.生成器本質(zhì)上就是一個(gè)函數(shù),它記住了上一次返回時(shí)在函數(shù)體中的位置。
?2.對(duì)生成器函數(shù)的第二次(或第n次)調(diào)用,跳轉(zhuǎn)到函數(shù)上一次掛起的位置。
?3.而且記錄了程序執(zhí)行的上下文。
?4.生成器不僅“記住”了它的數(shù)據(jù)狀態(tài),生成還記住了程序執(zhí)行的位置。
?(2)迭代器
?1.迭代器是一種支持next()操作的對(duì)象。它包含了一組元素,當(dāng)執(zhí)行next()操作時(shí),返回其中一個(gè)元素;
?2.當(dāng)所有元素都被返回后,再執(zhí)行next()報(bào)異?!猄topIteration;
?3.生成器一定是可迭代的,也一定是迭代器對(duì)象。
?(3)區(qū)別:
?1.生成器是生成元素的,迭代器是訪問(wèn)可迭代對(duì)象元素的一種方式;
?2.迭代輸出生成器的內(nèi)容;
?3.迭代器是一種支持next()操作的對(duì)象
?4.迭代器(iterator):其中iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流,可以把它看做一個(gè)有序序列,只是不知道序列的長(zhǎng)度,只有通過(guò)next()函數(shù)獲取需要計(jì)算的下一個(gè)數(shù)據(jù)??梢钥醋錾善鞯囊粋€(gè)子集。
?
?# 第12題
?生成器獲取value有兩種方式,一個(gè)是next()計(jì)算獲取下一個(gè)數(shù)據(jù),一個(gè)是yield,獲取外部傳來(lái)的值或暫停并返回函數(shù)的值
?
?# 第13題
?# 該題不需要文件,會(huì)自動(dòng)在程序目錄下創(chuàng)建web.log
?import time
?
?def logger(filename, channel='file'):
? ? ?"""
? ? ?日志方法
? ? ?:param filename: log filename
? ? ?:param channel: 輸出的目的地,屏幕(terminal),文件(file),屏幕+文件(both)
? ? ?:return:
? ? ?"""
? ? ?with open(filename, 'w', encoding='utf-8') as f:
? ? ? ? ?count = 1
? ? ? ? ?data_init = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + f' [{count}] test log db backup 3'
? ? ? ? ?f.write(data_init + '\n')
?
? ? ? ? ?while True:
? ? ? ? ? ? ?data = yield
? ? ? ? ? ? ?count += 1
? ? ? ? ? ? ?now = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
? ? ? ? ? ? ?log = now + f' [{count}] ' + data
?
? ? ? ? ? ? ?if channel == 'both':
? ? ? ? ? ? ? ? ?f.write(log + '\n')
? ? ? ? ? ? ? ? ?print(log)
? ? ? ? ? ? ?elif channel == 'file':
? ? ? ? ? ? ? ? ?f.write(log + '\n')
? ? ? ? ? ? ?elif channel == 'terminal':
? ? ? ? ? ? ? ? ?print(log)
? ? ? ? ? ? ?else:
? ? ? ? ? ? ? ? ?print('請(qǐng)檢查參數(shù)')
?
?# 調(diào)用
?log_obj = logger(filename='web.log', channel='both')
?log_obj.__next__()
?log_obj.send('user yz login success')
?log_obj.send('user zm login success')
?log_obj.send('user ghd login success')
?log_obj.send('user wzw login success') ? ? # 可以給多個(gè)send試試
?
?# 第14題
?name = ['alex', 'wupeiqi', 'yuanhao', 'nezha']
?print(list(map(lambda x: x + '_sb', name)))
?
?# 第15題
?list_1 = [1, 3, 5, 6, 7, 8]
?print(list(filter(lambda x: x % 2 == 0, list_1)))
?
?# 第16題
?1.我認(rèn)為是map()來(lái)算比較好:
?print(list(map(lambda x: x['shares'] * x['price'], portfolio)))
?2.代碼如下:
?portfolio = [
? ? ?{'name': 'IBM', 'shares': 100, 'price': 91.1},
? ? ?{'name': 'AAPL', 'shares': 50, 'price': 543.22},
? ? ?{'name': 'FB', 'shares': 200, 'price': 21.09},
? ? ?{'name': 'HPQ', 'shares': 35, 'price': 31.75},
? ? ?{'name': 'YHOO', 'shares': 45, 'price': 16.35},
? ? ?{'name': 'ACME', 'shares': 75, 'price': 115.65},
?]
?
?result = (filter(lambda x: x['price'] > 100, portfolio))
?print([i['name'] for i in result])
?
?# 第17題
?li = ['alex', 'egon', 'smith', 'pizza', 'alen']
?
?def func(old_str):
? ? ?if old_str[0] == 'a':
? ? ? ? ?new_str = 'A' + old_str[1:]
? ? ?else:
? ? ? ? ?new_str = old_str
? ? ?return new_str
?
?print(list(map(func, li)))
?
?# 第18題
?li = ['alex', 'egon', 'smith', 'pizza', 'alen']
?
?def rule(data):
? ? ?return data[-2]
?
?li.sort(key=rule, reverse=True)
?print(li)
?
?# 第19題
?# poetry.txt文件位于同目錄下
?import os
?count = 0
?
?with open('poetry.txt', 'r', encoding='utf-8') as f:
? ? ?with open('new.txt', 'w', encoding='utf-8') as new_f:
? ? ? ? ?while True:
? ? ? ? ? ? ?line = f.readline()
? ? ? ? ? ? ?if not line:
? ? ? ? ? ? ? ? ?break
? ? ? ? ? ? ?count += 1
? ? ? ? ? ? ?if count != 3:
? ? ? ? ? ? ? ? ?new_f.write(line)
? ? ? ? ? ? ? ? ?
?os.replace('new.txt', 'poetry.txt')
?
?# 第20題
?# username.txt文件位于同目錄下
?with open('username.txt', 'r+', encoding='utf-8') as f:
? ? ?data = f.readlines()
? ? ?for line in data:
? ? ? ? ?if 'alex' in line:
? ? ? ? ? ? ?print('用戶(hù)該用戶(hù)已存在')
? ? ? ? ? ? ?exit('程序結(jié)果')
? ? ?f.write('\nalex')
?
?# 第21題
?# user_info.txt文件位于同目錄下
?import os
?with open('user_info.txt', 'r', encoding='utf-8') as f:
? ? ?with open('new.txt', 'w', encoding='utf-8') as new_f:
? ? ? ? ?for line in f.readlines():
? ? ? ? ? ? ?if '100003' not in line:
? ? ? ? ? ? ? ? ?new_f.write(line)
?
?os.replace('new.txt', 'user_info.txt')
?
?# 第22題
?# user_info.txt文件位于同目錄下
?import os
?with open('user_info.txt', 'r', encoding='utf-8') as f:
? ? ?with open('new.txt', 'w', encoding='utf-8') as new_f:
? ? ? ? ?for line in f.readlines():
? ? ? ? ? ? ?if '100002' in line:
? ? ? ? ? ? ? ? ?line = 'alex li,100002'
? ? ? ? ? ? ?new_f.write(line)
?os.replace('new.txt', 'user_info.txt')
?
?# 第23題
?import time
?
?def time_calc(func):
? ? ?def inner(*args, **kwargs):
? ? ? ? ?begin = time.time()
? ? ? ? ?func(*args, **kwargs)
? ? ? ? ?end = time.time()
? ? ? ? ?return end-begin
? ? ?return inner
?
?@time_calc
?def calc(n):
? ? ?for i in range(n):
? ? ? ? ?pass # 占位符
?
?print('time:' + str(calc(1000000)))
?print('time:' + str(calc(10000000)))
?
?# 第24題
?lambda就是匿名函數(shù),自己經(jīng)常在map()、filter()中使用,實(shí)現(xiàn)一些簡(jiǎn)單的函數(shù)和三元運(yùn)算等。
?
?# 第25題
?import random
?
?def dice(n):
? ? ?temp_list = []
? ? ?for i in range(1, n+1):
? ? ? ? ?temp_list.append(random.randint(1, 7))
?
? ? ?return temp_list
?
?choice = input('三個(gè)骰子壓大?。ㄝ斎氪蠡蛐。?#39;).strip()
?if choice in ['大', '小']:
? ? ?result = dice(3)
? ? ?if sum(result) > 9:
? ? ? ? ?print(f'結(jié)果是:{result}={sum(result)},[大]!')
? ? ? ? ?if choice == '大':
? ? ? ? ? ? ?print('您壓中了!')
? ? ? ? ?else:
? ? ? ? ? ? ?print('您沒(méi)壓中!')
? ? ?elif sum(result) < 9:
? ? ? ? ?print(f'結(jié)果是:{result}={sum(result)},[小]!')
? ? ? ? ?if choice == '小':
? ? ? ? ? ? ?print('您壓中了!')
? ? ? ? ?else:
? ? ? ? ? ? ?print('您沒(méi)壓中!')
? ? ?else:
? ? ? ? ?print('不大不小。。。請(qǐng)?jiān)賮?lái)一遍!')
附錄:
?# 第2題的test.txt內(nèi)容如下:
?py, 18, 177333112233
?py, 24, 18845516854
?zm, 18, 177333112233
?py, 24, 18845516854
?zm, 18, 177333112233
?zm, 24, 18845516854
?py, 18, 177333112233
?zm, 24, 18845516854
?
?# 第10題的test.txt內(nèi)容如下:
?zm,123
?ghd,123
?wzw,123
?py,123
?yz,123
?
?# 第19題的poetry.txt內(nèi)容如下:
?昔人已乘黃鶴去,此地空余黃鶴樓。
?黃鶴一去不復(fù)返,白云千載空悠悠。
?晴川歷歷漢陽(yáng)樹(shù),芳草萋萋鸚鵡洲。
?日暮鄉(xiāng)關(guān)何處是?煙波江上使人愁。
?
?# 第20題的username.txt內(nèi)容如下:
?pizza
?alex
?egon
?
?# 第21和22題的user_info.txt內(nèi)容如下:
?pizza,100001
?alex,100002
?egon,100003
末
有什么Python代碼或者其他問(wèn)題可以私信問(wèn)我或者在下面留言,Python課程設(shè)計(jì)我也偶爾可以有償幫做,祝大家變得更強(qiáng)[狗頭]
剩下的就是和上一篇文章末尾一樣要說(shuō)的,我就當(dāng)成套話(huà)了。
套話(huà):因?yàn)樾∑普旧系奈谋靖袷綄?duì)演示代碼極其不友好,而且自己平時(shí)的筆記是通過(guò)Markdown語(yǔ)法來(lái)記錄的,在格式上和美觀程度上不是很好看,如果你看的不習(xí)慣,就去下載一個(gè)Typora(或者支持markdown語(yǔ)法的應(yīng)用),我這里給出md文件的迅雷網(wǎng)盤(pán)鏈接(百度網(wǎng)盤(pán)上傳不了),然后用Typora打開(kāi)文件看就好了。
鏈接:https://pan.xunlei.com/s/VMRrPrc3smR1gUzD5Lmu_QDoA1
提取碼:prqj