若想技術(shù)精進(jìn)调榄,當(dāng)然得把基礎(chǔ)知識(shí)打得牢牢的踊赠。
廖雪峰的官方網(wǎng)站 ?python3教程,該網(wǎng)站提供的教程淺顯易懂每庆,還附帶了講學(xué)視頻筐带,非常適合初學(xué)者正規(guī)入門(mén)。
以下是通過(guò)廖雪峰python官方網(wǎng)站學(xué)習(xí)的個(gè)人查漏補(bǔ)缺缤灵。
主要內(nèi)容包括:1.切片 ?2.迭代 ?3.列表生成式 ?4.生成器 ?5.迭代器 ?6.函數(shù)式編程 ?7.map/reduce ?8.filter ?9.sorted ?10.返回函數(shù) ?11.匿名函數(shù) ?12.裝飾器 ?13.偏函數(shù)
1. 切片
切片:Python的切片非常靈活伦籍,一行代碼就可以實(shí)現(xiàn)很多行循環(huán)才能完成的操作。 ?
>>>?L = ['Michael','Sarah','Tracy','Bob','Jack'] ? ??
>>> L[0:3]
['Michael', 'Sarah', 'Tracy']
>>> L[:3]
['Michael', 'Sarah', 'Tracy']
L[0:3]表示腮出,從索引0開(kāi)始取帖鸦,直到索引3為止,但不包括索引3胚嘲。即索引0作儿,1,2馋劈,正好是3個(gè)元素立倍。如果第一個(gè)索引是0,還可以省略:L[:3]侣滩。
>>> L = list(range(100))
>>> L[-10:] ? ##取后10個(gè)
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
>>> L[:10:2] ? ##前10個(gè)數(shù),每?jī)蓚€(gè)取一個(gè)
[0, 2, 4, 6, 8]
>>> L[::5] ? ##所有數(shù)变擒,每5個(gè)取一個(gè)
[0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95]
tuple也是一種list君珠,唯一區(qū)別是tuple不可變。因此娇斑,tuple也可以用切片操作策添,只是操作的結(jié)果仍是tuple材部。
字符串'xxx'也可以看成是一種list,每個(gè)元素就是一個(gè)字符唯竹。因此乐导,字符串也可以用切片操作,只是操作結(jié)果仍是字符串浸颓。
2.迭代
很多語(yǔ)言比如C語(yǔ)言物臂,迭代list是通過(guò)下標(biāo)完成的,比如Java代碼:
for (i=0; i<list.length; i++) {
? ? n=list[i];
}
Python對(duì)list列表和tuple元組迭代通過(guò) ?for ... in 來(lái)完成的产上。 ? 注:list這種數(shù)據(jù)類(lèi)型雖然有下標(biāo)棵磷,但很多其他數(shù)據(jù)類(lèi)型是沒(méi)有下標(biāo)的,但是晋涣,只要是可迭代對(duì)象仪媒,無(wú)論有無(wú)下標(biāo),都可以迭代谢鹊,比如dict就可以迭代算吩。
如何判斷一個(gè)對(duì)象是可迭代對(duì)象?方法是通過(guò)collections模塊的Iterable類(lèi)型判斷:
>>> from collections import Iterable
>>> isinstance('abc', Iterable) # str是否可迭代
True
>>> isinstance([1,2,3], Iterable) # list是否可迭代
True
>>> isinstance(123, Iterable) # 整數(shù)是否可迭代
False
Python對(duì)dict字典的迭代佃扼,默認(rèn)情況下迭代的是key: ?for key in dictionary 來(lái)完成偎巢。 ?如果要迭代value,可以使用 for value in dictinary.values() 來(lái)完成松嘶。 ?如果要同時(shí)迭代key和vaule艘狭,可以使用 ?for k, v in dictionary.items() 來(lái)完成。
d={'a':1, 'b':2, 'c':3}
for key in d: ?#默認(rèn)情況下用key迭代
for value in d.values(): ?#使用value來(lái)迭代
for k, v in d.items(): ?#使用key和vaule同時(shí)迭代
如果要對(duì)list實(shí)現(xiàn)類(lèi)似Java那樣的下標(biāo)循環(huán)怎么辦翠订?Python內(nèi)置的enumerate函數(shù)可以把一個(gè)list變成索引-元素對(duì)巢音,這樣就可以在for循環(huán)中同時(shí)迭代索引和元素本身:
3.列表生成式
列表生成式:列表生成式即List Comprehensions官撼,是Python內(nèi)置的非常簡(jiǎn)單卻強(qiáng)大的可以用來(lái)創(chuàng)建list的生成式。
####生成 list:[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>>list(range(1,11))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
####生成 list :[1x1, 2x2, 3x3, ..., 10x10]
>>>[x * x for x in range(1,11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
####for循環(huán)后面還可以加上if判斷,可以篩選出僅偶數(shù)的平方
>>> [x * x for x in range(1, 11) if x % 2 == 0]
[4, 16, 36, 64, 100]
####生成全排列
>>> [m + n for m in 'ABC' for n in 'XYZ']
['AX', 'AY', 'AZ', 'BX', 'BY', 'BZ', 'CX', 'CY', 'CZ']
運(yùn)用列表生成式似谁,可以寫(xiě)出非常簡(jiǎn)潔的代碼傲绣。例如,列出當(dāng)前目錄下的所有文件和目錄名巩踏,可以通過(guò)一行代碼實(shí)現(xiàn):
>>> import os # 導(dǎo)入os模塊
>>> [d for d in os.listdir('.')] ? ? # os.listdir可以列出文件和目錄
###列表生成式也可以使用兩個(gè)變量來(lái)生成list:
>>> d = {'x': 'A', 'y': 'B', 'z': 'C' }
>>> [k + '=' + v for k, v in d.items()]
['y=B', 'x=A', 'z=C']
###把一個(gè)list中所有的字符串變成小寫(xiě):
>>> L = ['Hello', 'World', 'IBM', 'Apple']
>>> [s.lower() for s in L]
['hello', 'world', 'ibm', 'apple']
運(yùn)用列表生成式秃诵,可以快速生成list,可以通過(guò)一個(gè)list推導(dǎo)出另一個(gè)list塞琼,而代碼卻十分簡(jiǎn)潔菠净。
4.生成器
為什么是生成器??毅往?——通過(guò)列表生成式牵咙,我們可以直接創(chuàng)建一個(gè)列表。但是攀唯,受到內(nèi)存限制洁桌,列表容量肯定是有限的。而且侯嘀,創(chuàng)建一個(gè)包含100萬(wàn)個(gè)元素的列表另凌,不僅占用很大的存儲(chǔ)空間,如果我們僅僅需要訪問(wèn)前面幾個(gè)元素残拐,那后面絕大多數(shù)元素占用的空間都白白浪費(fèi)了途茫。
所以,如果列表元素可以按照某種算法推算出來(lái)溪食,那我們是否可以在循環(huán)的過(guò)程中不斷推算出后續(xù)的元素呢囊卜?這樣就不必創(chuàng)建完整的list,從而節(jié)省大量的空間错沃。在Python中栅组,這種一邊循環(huán)一邊計(jì)算的機(jī)制,稱(chēng)為生成器:generator枢析。
要?jiǎng)?chuàng)建一個(gè)generator玉掸,有很多種方法。第一種方法很簡(jiǎn)單醒叁,只要把一個(gè)列表生成式的[ ]改成( )司浪,就創(chuàng)建了一個(gè)generator:
>>> L = [x * x for x in range(10)] ? ## [ ] 列表生成式
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10)) ?##( ) 生成器
>>> g
<generator object <genexpr> at 0x1022ef630>
####如果要一個(gè)一個(gè)打印出來(lái),可以通過(guò)next()函數(shù)獲得generator的下一個(gè)返回值:
>>> next(g)
0
####生成器generator也是可迭代對(duì)象把沼,使用for循環(huán)迭代
>>> g = (x * x for x in range(10))
>>> for n in g:
... ? ? print(n)
...?
我們創(chuàng)建了一個(gè)generator后啊易,基本上永遠(yuǎn)不會(huì)調(diào)用next(),而是通過(guò)for循環(huán)來(lái)迭代它饮睬,并且不需要關(guān)心StopIteration的錯(cuò)誤租谈。generator非常強(qiáng)大。如果推算的算法比較復(fù)雜捆愁,用類(lèi)似列表生成式的for循環(huán)無(wú)法實(shí)現(xiàn)的時(shí)候割去,還可以用函數(shù)來(lái)實(shí)現(xiàn)。
斐波拉契數(shù)列用列表生成式寫(xiě)不出來(lái)昼丑,但是呻逆,用函數(shù)把它打印出來(lái)卻很容易:
fib函數(shù)實(shí)際上是定義了斐波拉契數(shù)列的推算規(guī)則菩帝,可以從第一個(gè)元素開(kāi)始咖城,推算出后續(xù)任意的元素憔足,這種邏輯其實(shí)非常類(lèi)似generator。也就是說(shuō)酒繁,上面的函數(shù)和generator僅一步之遙。要把fib函數(shù)變成generator控妻,只需要把print(b)改為yield b就可以了:
這里州袒,最難理解的就是generator和函數(shù)的執(zhí)行流程不一樣。函數(shù)是順序執(zhí)行弓候,遇到return語(yǔ)句或者最后一行函數(shù)語(yǔ)句就返回郎哭。而變成generator的函數(shù),在每次調(diào)用next()的時(shí)候執(zhí)行菇存,遇到y(tǒng)ield語(yǔ)句返回夸研,再次執(zhí)行時(shí)從上次返回的yield語(yǔ)句處繼續(xù)執(zhí)行。
5.迭代器
可以直接作用于for循環(huán)的數(shù)據(jù)類(lèi)型有以下幾種:
一類(lèi)是集合數(shù)據(jù)類(lèi)型依鸥,如list列表亥至、tuple元組、dict字典贱迟、set集合姐扮、str字符串等;
一類(lèi)是generator衣吠,包括生成器和帶yield的generator function茶敏。
這些可以直接作用于for循環(huán)的對(duì)象統(tǒng)稱(chēng)為可迭代對(duì)象:Iterable。
注:生成器都是Iterator對(duì)象缚俏,但list惊搏、dict、str雖然是Iterable忧换,卻不是Iterator恬惯。
把list、dict包雀、str等Iterable變成Iterator可以使用iter()函數(shù):
>>> isinstance(iter([]), Iterator)
True
>>> isinstance(iter('abc'), Iterator)
True
為什么list宿崭、dict、str等數(shù)據(jù)類(lèi)型不是Iterator迭代器對(duì)象才写?
這是因?yàn)镻ython的Iterator對(duì)象表示的是一個(gè)數(shù)據(jù)流葡兑,Iterator對(duì)象可以被next()函數(shù)調(diào)用并不斷返回下一個(gè)數(shù)據(jù),直到?jīng)]有數(shù)據(jù)時(shí)拋出StopIteration錯(cuò)誤赞草《锏蹋可以把這個(gè)數(shù)據(jù)流看做是一個(gè)有序序列,但我們卻不能提前知道序列的長(zhǎng)度厨疙,只能不斷通過(guò)next()函數(shù)實(shí)現(xiàn)按需計(jì)算下一個(gè)數(shù)據(jù)洲守,所以Iterator的計(jì)算是惰性的,只有在需要返回下一個(gè)數(shù)據(jù)時(shí)它才會(huì)計(jì)算。Iterator甚至可以表示一個(gè)無(wú)限大的數(shù)據(jù)流梗醇,例如全體自然數(shù)知允。而使用list是永遠(yuǎn)不可能存儲(chǔ)全體自然數(shù)的。
凡是可作用于for循環(huán)的對(duì)象都是Iterable類(lèi)型叙谨;凡是可作用于next()函數(shù)的對(duì)象都是Iterator類(lèi)型温鸽,它們表示一個(gè)惰性計(jì)算的序列;集合數(shù)據(jù)類(lèi)型如list手负、dict涤垫、str等是Iterable但不是Iterator,不過(guò)可以通過(guò)iter()函數(shù)獲得一個(gè)Iterator對(duì)象竟终;Python的for循環(huán)本質(zhì)上就是通過(guò)不斷調(diào)用next()函數(shù)實(shí)現(xiàn)的蝠猬。
小結(jié)
1)切片:list、tuple统捶、str都可以切片榆芦,且切片操作非常靈活、代碼簡(jiǎn)單瘾境。
2)迭代:使用for...in xxx: 語(yǔ)句歧杏, list、tuple迷守、set犬绒、dict、str都是可迭代的兑凿。凡是可作用于for循環(huán)的對(duì)象都是Iterable類(lèi)型凯力。
3)列表生成式:使用簡(jiǎn)單語(yǔ)句生成列表[ ]
4)生成器:最簡(jiǎn)單的方法,將列表生成式[ ] 改為( ) 就是一個(gè)生成器礼华; ?將函數(shù)改為生成器函數(shù)咐鹤,使用yield,每調(diào)用一次都會(huì)在這暫時(shí)停留一下圣絮,你不再調(diào)用祈惶,我就不再走。一般使用for循環(huán)遍歷扮匠。
5)迭代器:凡是可作用于next()函數(shù)的對(duì)象都是Iterator類(lèi)型捧请,它們表示一個(gè)惰性計(jì)算的序列。
6.函數(shù)式編程
函數(shù)式編程就是一種抽象程度很高的編程范式棒搜,純粹的函數(shù)式編程語(yǔ)言編寫(xiě)的函數(shù)沒(méi)有變量疹蛉,因此,任意一個(gè)函數(shù)力麸,只要輸入是確定的可款,輸出就是確定的育韩,這種純函數(shù)我們稱(chēng)之為沒(méi)有副作用。而允許使用變量的程序設(shè)計(jì)語(yǔ)言闺鲸,由于函數(shù)內(nèi)部的變量狀態(tài)不確定筋讨,同樣的輸入,可能得到不同的輸出摸恍,因此版仔,這種函數(shù)是有副作用的。
函數(shù)式編程的一個(gè)特點(diǎn)就是误墓,允許把函數(shù)本身作為參數(shù)傳入另一個(gè)函數(shù),還允許返回一個(gè)函數(shù)益缎!
1)變量可以指向函數(shù)
>>>f=abs ?###變量f谜慌,指向函數(shù)abs
>>>f(-10)###直接調(diào)用abs()函數(shù)和調(diào)用變量f()完全相同
10
2)函數(shù)名也是變量
>>> abs = 10
>>> abs(-10) ? ?###abs已經(jīng)被更改為整數(shù),其函數(shù)功能就不能用了
Traceback (most recent call last):
????File "<stdin>", line 1, in <module>
TypeError: 'int' object is not callable
3)傳入函數(shù)
既然變量可以指向函數(shù)莺奔,函數(shù)的參數(shù)能接收變量欣范,那么一個(gè)函數(shù)就可以接收另一個(gè)函數(shù)作為參數(shù),這種函數(shù)就稱(chēng)之為高階函數(shù)令哟。
def add(x, y, f):
????return f(x) + f(y)
x = -5
y = 6
f = absf(x) + f(y) ==> abs(-5) + abs(6) ==> 11
把函數(shù)作為參數(shù)傳入恼琼,這樣的函數(shù)稱(chēng)為高階函數(shù),函數(shù)式編程就是指這種高度抽象的編程范式屏富。
7.map/reduce
Python內(nèi)置map()和reduce()函數(shù)晴竞。
map()高階函數(shù)接收兩個(gè)參數(shù),一個(gè)是函數(shù)狠半,一個(gè)是Iterable噩死,map將傳入的函數(shù)依次作用到序列的每個(gè)元素(map函數(shù)本質(zhì),函數(shù)一一作用于元素)神年,并把結(jié)果作為新的Iterator返回已维。
>>> def f(x):
... ???? return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) ?#map(函數(shù)名,可迭代對(duì)象) 函數(shù)一一作用于可迭代對(duì)象中的元素已日,最后返回一個(gè)迭代器:Iterator惰性序列 ? ?r垛耳。
>>> list(r) ? ?###Iterator迭代器(惰性序列),使用list()將整個(gè)序列都計(jì)算出來(lái)飘千,并返回一個(gè)list堂鲜。
[1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce()把一個(gè)函數(shù)作用在一個(gè)序列[x1, x2, x3, ...]上,這個(gè)函數(shù)必須接收兩個(gè)參數(shù)占婉,reduce把結(jié)果繼續(xù)和序列的下一個(gè)元素做累積計(jì)算(reduce函數(shù)本質(zhì)泡嘴,函數(shù)把結(jié)果再代入充當(dāng)其中的一個(gè)參數(shù)),其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
###reduce例子
>>> from functools import reduce
>>> def fn(x, y):
... ???? return x * 10 + y
...
>>> reduce(fn, [1, 3, 5, 7, 9])
13579
8.filter
Python內(nèi)置filter()函數(shù)用于過(guò)濾序列逆济。
和map()類(lèi)似酌予,filter()也接收一個(gè)函數(shù)和一個(gè)序列磺箕。和map()不同的是,filter()把傳入的函數(shù)依次作用于每個(gè)元素抛虫,然后根據(jù)返回值是True還是False決定保留還是丟棄該元素(filter函數(shù)本質(zhì)松靡,一一作用于元素,True/False決定是否保留)建椰。
def is_odd(n):
????return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 結(jié)果: [1, 5, 9, 15]
可見(jiàn)用filter()這個(gè)高階函數(shù)雕欺,關(guān)鍵在于正確實(shí)現(xiàn)一個(gè)“篩選”函數(shù),即能夠返回True/False的函數(shù)棉姐。filter()函數(shù)返回的是一個(gè)Iterator迭代器屠列,也就是一個(gè)惰性序列,所以要強(qiáng)迫f(wàn)ilter()完成計(jì)算結(jié)果伞矩,需要用list()函數(shù)獲得所有結(jié)果并返回list笛洛。
生成素?cái)?shù):
def _odd_iter():###定義一個(gè)奇數(shù)生成器,生成無(wú)限序列
? ? n=1
? ? while True:
? ? ? ? n=n+2
? ? ? ? yield n
############################################
def _not_divisible(n):###定義一個(gè)篩選函數(shù)
? ? return lambda x: x%n>0
############################################
def primes():###定義一個(gè)生成器乃坤,不斷返回下一個(gè)素?cái)?shù)
? ? yield 2
? ? it=_odd_iter() #初始序列
? ? while True:
? ? ? ? n=next(it) #返回序列的第一個(gè)數(shù)
? ? ? ? yield n
? ? ? ? it=filter(_not_divisible(n), it) #構(gòu)造新序列
############################################
#打印100以?xún)?nèi)的素?cái)?shù):
for n in primes():
? ? if n<1000:
? ? ? ? print(n)
? ? else:
break
9.sorted
sorted()函數(shù)也是一個(gè)高階函數(shù)苛让,它還可以接收一個(gè)key函數(shù)來(lái)實(shí)現(xiàn)自定義的排序,例如按絕對(duì)值大小排序:
>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]
############################################
##### 給sorted傳入key函數(shù)湿诊,即可實(shí)現(xiàn)忽略大小寫(xiě)的排序:
>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']
sorted()也是一個(gè)高階函數(shù)狱杰。用sorted()排序的關(guān)鍵在于實(shí)現(xiàn)一個(gè)映射函數(shù)。高階函數(shù)的抽象能力是非常強(qiáng)大的厅须,而且仿畸,核心代碼可以保持得非常簡(jiǎn)潔。
10.返回函數(shù)
函數(shù)作為返回值:高階函數(shù)除了可以接受函數(shù)作為參數(shù)外朗和,還可以把函數(shù)作為結(jié)果值返回颁湖。
def lazy_sum(*args):
? ? def sum():
? ? ? ? ax=0
? ? ? ? for n in args:
? ? ? ? ? ? ax=ax+n
? ????? return ax
? ? return sum ?##lazy_sum()函數(shù)的返回值為一個(gè)函數(shù)sum()
內(nèi)部函數(shù)sum()可以引用外部函數(shù)lazy_sum的參數(shù)和局部變量,當(dāng)lazy_sum返回函數(shù)sum時(shí)例隆,相關(guān)參數(shù)和變量都保存在返回的函數(shù)中甥捺,這種稱(chēng)為“閉包(Closure)”的程序結(jié)構(gòu)擁有極大的威力。再注意一點(diǎn)镀层,當(dāng)我們調(diào)用lazy_sum()時(shí)镰禾,每次調(diào)用都會(huì)返回一個(gè)新的函數(shù),即使傳入相同的參數(shù)唱逢。
11.匿名函數(shù)
當(dāng)我們?cè)趥魅牒瘮?shù)時(shí)吴侦,有些時(shí)候,不需要顯式地定義函數(shù)坞古,直接傳入匿名函數(shù)更方便备韧。
>>> list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
關(guān)鍵字 lambda 表示匿名函數(shù), 冒號(hào)前面的 x 表示函數(shù)參數(shù)痪枫。
匿名函數(shù)有個(gè)限制织堂,就是只能有一個(gè)表達(dá)式叠艳,不用寫(xiě)return,返回值就是該表達(dá)式的結(jié)果易阳。用匿名函數(shù)有個(gè)好處附较,因?yàn)楹瘮?shù)沒(méi)有名字,不必?fù)?dān)心函數(shù)名沖突潦俺。此外拒课,匿名函數(shù)也是一個(gè)函數(shù)對(duì)象,也可以把匿名函數(shù)賦值給一個(gè)變量事示,再利用變量來(lái)調(diào)用該函數(shù)早像。Python對(duì)匿名函數(shù)的支持有限,只有一些簡(jiǎn)單的情況下可以使用匿名函數(shù)肖爵。
12.裝飾器
為什么有裝飾器扎酷??
1)由于函數(shù)也是一個(gè)對(duì)象遏匆,而且函數(shù)對(duì)象可以被賦值給變量,所以谁榜,通過(guò)變量也能調(diào)用該函數(shù)幅聘。 ? ?----能懂
eg:abs是python的內(nèi)置函數(shù), f=abs ? 則窃植,f(-11) ?等同于 abs(-11)
2)函數(shù)對(duì)象有一個(gè)__name__屬性帝蒿,可以拿到函數(shù)的名字。 ? ----能懂
eg:abs函數(shù)巷怜,通過(guò)調(diào)用這個(gè)函數(shù)的name屬性葛超,可以得到其函數(shù)名,abs.__name__ ? ? ? ?f也可以 ?f.__name__
3)假設(shè)要增強(qiáng) abs() 函數(shù)的功能延塑,比如绣张,在函數(shù)調(diào)用前后自動(dòng)打印日志,但又不希望修改 abs() 函數(shù)的定義关带,這種在代碼運(yùn)行期間動(dòng)態(tài)增加功能的方式侥涵,稱(chēng)之為“裝飾器”(Decorator)。 ? ? ? ----不是很懂
本質(zhì)上宋雏,decorator就是一個(gè)返回函數(shù)的高階函數(shù)芜飘。所以,我們要定義一個(gè)能打印日志的decorator磨总,可以定義如下:
####定義一個(gè)打印日志的迭代器####
def log(func): ? ?###接收一個(gè)函數(shù)作為輸入?yún)?shù)
? ? def wrapper(*args, **kw):###定義函數(shù)
? ? ? ? print('call %s():' %func.__name__)
? ? ? ? return func(*args, **kw) ?###返回輸入的函數(shù)
? ? return wrapper ? ####decorator裝飾器的本質(zhì)就是返回函數(shù)的高階函數(shù)嗦明,所以此裝飾器要返回函數(shù) ?wrapper
我們要借助Python的@語(yǔ)法,把decorator置于函數(shù)的定義處:
@log ?##借助@裝飾器名 ?將其置于函數(shù)定義處 ?相當(dāng)于abs=log(abs)
def abs():
### 由于log()是一個(gè)decorator蚪燕,返回一個(gè)函數(shù),所以,原來(lái)的abs()函數(shù)仍然存在设哗,只是現(xiàn)在同名的abs變量指向了新的函數(shù)屋群,于是調(diào)用abs()將執(zhí)行新函數(shù),即在log()函數(shù)中返回的wrapper()函數(shù)屁商。
然后再調(diào)用 abs() 函數(shù),不僅會(huì)運(yùn)行 abs() 函數(shù)本身,還會(huì)在運(yùn)行abs() 函數(shù)前打印一行日志营勤。
一個(gè)完整的decorator的寫(xiě)法:
import functools
def log(func):
? ? @functools.wraps(func)#重要,寫(xiě)裝飾器的時(shí)候加著壹罚,防止原函數(shù)的__name__屬性值被修改
? ? def wrapper(*args, **kw):
? ? ? ? print('call %s():' %func.__name__)
? ? ? ? return func(*args, **kw)
? ? return wrapper
帶有參數(shù)的decorator的寫(xiě)法:
import functools
def log(text):
? ? def decorator(func):
? ? ? ? @functools.wraps(func)
? ? ? ? def wrapper(*args, **kw):
? ? ? ? ? ? print('%s %s():' %(text, func.__name__))
? ? ? ? ? ? return func(*agrs, **kw)
? ? ? ? return wrapper
? ? return decorator
在面向?qū)ο螅∣OP)的設(shè)計(jì)模式中葛作,decorator被稱(chēng)為裝飾模式。OOP的裝飾模式需要通過(guò)繼承和組合來(lái)實(shí)現(xiàn)猖凛,而Python除了能支持OOP的decorator外赂蠢,直接從語(yǔ)法層次支持decorator。Python的decorator可以用函數(shù)實(shí)現(xiàn)辨泳,也可以用類(lèi)實(shí)現(xiàn)虱岂。
decorator可以增強(qiáng)函數(shù)的功能,定義起來(lái)雖然有點(diǎn)復(fù)雜菠红,但使用起來(lái)非常靈活和方便第岖。
13.偏函數(shù)
當(dāng)函數(shù)的參數(shù)個(gè)數(shù)太多,需要簡(jiǎn)化時(shí)试溯,使用functools.partial可以創(chuàng)建一個(gè)新的函數(shù)蔑滓,這個(gè)新函數(shù)可以固定住原函數(shù)的部分參數(shù),從而在調(diào)用時(shí)更簡(jiǎn)單遇绞。
int2 = functools.partial(int, base=2) ?#固定參數(shù) ? 轉(zhuǎn)換成二進(jìn)制
max2 = functools.partial(max, 10) ?# 實(shí)際上會(huì)把10作為*args的一部分自動(dòng)加到左邊
##max2(5,6,7) ?等價(jià)于 ?args=(10,5,6,7) ? max(*args)