最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

3rd Python函數(shù)基礎(chǔ)及進(jìn)階

2021-01-25 10:57 作者:MoNanGo  | 我要投稿

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)比理解一下),adataid()都一樣,那我們修改一下原來(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)),所以我們修改容器里面的值,adata兩個(gè)指針指向都沒(méi)變,還是指向容器地址,共享同一份數(shù)據(jù)。二字符串那些不可變數(shù)據(jù)類(lèi)型修改一下值就會(huì)創(chuàng)建一個(gè)新的數(shù)據(jù),所以有指針的指向會(huì)發(fā)生變化。

那特定情況下,比如數(shù)據(jù)備份,我們不想讓adata同時(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):

  1. 調(diào)用函數(shù)傳參時(shí),位置參數(shù)必須在關(guān)鍵參數(shù)的前面,比如register(major='math', 18, name='wzw')就·會(huì)報(bào)錯(cuò)SyntaxError: positional argument follows keyword argument

  2. 很容易產(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或者**kwargsargs就是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()輸出自己的局部變量namechange_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ù),像上面的changechange2都是函數(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ù)如下圖所示:

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:ghd

  • eval():可以把字符串形式的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)空間互不干擾,localsglobals兩個(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)型有以下幾種:

  1. 集合數(shù)據(jù)類(lèi)型,eg:list、tuple、dict、set、str等

  2. 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


3rd Python函數(shù)基礎(chǔ)及進(jìn)階的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
类乌齐县| 古浪县| 平原县| 孝感市| 两当县| 赫章县| 沈丘县| 乌拉特中旗| 平果县| 临邑县| 沈阳市| 宜君县| 辽中县| 和硕县| 钦州市| 怀远县| 永济市| 普定县| 逊克县| 岐山县| 沧源| 高青县| 江山市| 涞源县| 龙门县| 顺平县| 沙河市| 信丰县| 紫阳县| 会昌县| 师宗县| 泊头市| 南乐县| 石屏县| 东丽区| 福安市| 盐亭县| 永靖县| 稻城县| 林周县| 麦盖提县|