筆記更新于2019年11月19日硫豆,
摘要:返回函數(shù);閉包的介紹笼呆;裝飾器的工作原理解析
寫在前面:為了更好的學習python熊响,博主記錄下自己的學習路程。本學習筆記基于廖雪峰的Python教程诗赌,如有侵權汗茄,請告知刪除。歡迎與博主一起學習Pythonヽ( ̄▽ ̄)?
文章目錄
高階函數(shù)
返回函數(shù)
閉包
裝飾器
? 初步的裝飾器
? 完整裝飾器的構建
? 帶參數(shù)的裝飾器
高階函數(shù)
返回函數(shù)
高階函數(shù)除了可以把函數(shù)作為參數(shù)之外境肾,還能把函數(shù)作為返回值返回剔难。
如定義一個函數(shù),在函數(shù)內部再定義一個函數(shù)奥喻,而外面的函數(shù)把內部函數(shù)作為返回值偶宫。
def sum(*args):
def sum1():
a = 0
for n in args:
a = a + n
return a
return sum1
我們執(zhí)行sum( )這個函數(shù)
>>>f = sum(1, 2, 3)
>>>f
<function sum.<locals>.sum1 at 0x00000000021E37B8>
會發(fā)現(xiàn)返回的是一個函數(shù)sum1,而不是整數(shù)1环鲤,2纯趋,3的求和結果。我們需要調用函數(shù)f( )才能求出結果冷离。
>>>f()
6
需要注意的是吵冒,每次調用sum( ),都會返回一個新的函數(shù)西剥,即使傳入的參數(shù)是相同的痹栖。
>>>f1 = sum(1, 2, 3)
>>>f2 = sum(1, 2, 3)
>>>f1 == f2
False
閉包
在上面的例子中,我們稱sum為外部函數(shù)瞭空,sum1為內部函數(shù)揪阿,內部函數(shù)可以引用外部函數(shù)的參數(shù)和局部變量,而當外部函數(shù)返回內部函數(shù)時咆畏,參數(shù)和局部變量都保存在返回的函數(shù)中南捂,這種程序結構稱為閉包。
需要注意的是旧找,返回的函數(shù)并沒有立刻執(zhí)行直到被調用溺健,因此在返回函數(shù)中最好不要引用任何循環(huán)變量或者后續(xù)會發(fā)生變化的變量,看個例子(以下例子轉自廖雪峰的官方網(wǎng)站)钮蛛。
def count():
fs = []
for i in range(1, 4):
def f():
return i*i
fs.append(f)
return fs
f1, f2, f3 = count()
輸出結果
>>> f1()
9
>>> f2()
9
>>> f3()
9
結果不是認為中的1鞭缭,4,9愿卒,那是因為返回函數(shù)f引用的參數(shù)是i缚去,而等到函數(shù)執(zhí)行的時候,i等于3琼开,因此f1易结,f2,f3的結果都是9柜候「愣可以再定義一個函數(shù)來解決這個問題,但會略顯麻煩渣刷。
def count():
def f(j):
def g():
return j*j
return g
fs = []
for i in range(1, 4):
fs.append(f(i)) # f(i)立刻被執(zhí)行鹦肿,因此i的當前值被傳入f(),返回的是函數(shù)g
return fs
輸出結果
>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
4
>>> f3()
9
裝飾器 Decorator
Decorator辅柴,在英文上是裝飾師的意思箩溃。顧名思義瞭吃,裝飾器的作用就是裝飾,在函數(shù)不需要做任何代碼變動的前提下增加額外功能涣旨,增加比如插入日志歪架、性能測試、權限校驗等場景霹陡。實質上和蚪,Decorator是一個高階函數(shù),它傳入一個函數(shù)烹棉,又返回一個函數(shù)攒霹,形成閉包的結構。下面將用一個簡單的例子解釋裝飾器的工作原理浆洗。
首先我們定義兩個簡單的函數(shù)催束。
def Ming():
print('I am Ming.')
def Hong():
print('I am Hong.')
在解釋之前插入一個小知識點,每個函數(shù)都有_name_屬性辅髓,可以拿到函數(shù)的名字泣崩,如:
>>>f = Ming
>>>Ming.__name__
Ming
>>>f.__name__ #這里由于函數(shù)MIng賦給變量f,所f的__name__屬性也是Ming洛口。
Ming
再跟隨上面的例子矫付,我們希望在執(zhí)行Ming函數(shù)時還能打印出函數(shù)的執(zhí)行日志,于是可這樣添加語句第焰。
def Ming():
print('Ming is running.')
print('I am Ming.')
運行結果
>>>Ming()
Ming is running.
I am Ming.
那如果Hong也要這樣打印執(zhí)行日志呢买优,如果要每個函數(shù)都去修改的話就太麻煩了,于是我們可以先定義一個函數(shù)挺举,函數(shù)的參數(shù)是要執(zhí)行的函數(shù)杀赢,內容為先打印出執(zhí)行日志,再執(zhí)行需要的函數(shù)湘纵,像這樣:
def run(func):
print('%s is running' % func.__name__)
func()
return
def Ming():
print('I am Ming.')
return
運行結果
>>>run(Ming)
Ming is running
I am Ming.
>>>run(Hong)
Hong is running
I am Hong.
? 初步的裝飾器
但是這樣的話脂崔,每次都要執(zhí)行run函數(shù),顯得麻煩而且我們本來是執(zhí)行Ming函數(shù)的梧喷,這樣就破壞了代碼的邏輯性砌左。如果用裝飾器的話就能簡單地解決這些問題了。下面是一個簡單的裝飾器铺敌。
def run(func):
def wrapper(*args, **kw): #wrapper在英文中是包裝紙的意思
print('%s is runing.' % func.__name__)
return func(*args, **kw)
return wrapper
def Ming():
print('I am Ming.')
Ming = run(Ming)
運行一下Ming函數(shù)汇歹。
>>>Ming()
Ming is runing.
I am Ming.
解釋一下上面裝飾器的運行過程。但我們執(zhí)行run(Ming)時偿凭,返回的是wrapper函數(shù)产弹,而根據(jù)wrapper函數(shù)的定義,它接收任意可變參數(shù)和關鍵字參數(shù)弯囊,然后執(zhí)行print痰哨,打印出執(zhí)行日志胶果,然后返回func與其對應的參數(shù),即返回Ming斤斧。所以最終的結果相當于打印出了執(zhí)行日志稽物,然后執(zhí)行Ming函數(shù)。
? 完整裝飾器的構建
再者折欠,我們可以借助Python中的@符號,來避免每次定義函數(shù)后都要進行函數(shù)賦值的操作吼过。在定義新的函數(shù)前加入@run即可锐秦,@run相當于執(zhí)行了Ming = run(Ming)語句,像這樣:
def run(func):
def wrapper(*args, **kw):
print('%s is runing.' % func.__name__)
return func(*args, **kw)
return wrapper
@run
def Ming():
print('I am Ming.')
>>>Ming()
Ming is runing.
I am Ming.
運行結果與上面一樣盗忱。
到目前為止酱床,裝飾器的建立差不多完成,但還有一個問題:在運行過程中趟佃,我們把原函數(shù)的信息給替換了扇谣,比如Ming的_name_屬性。
>>>Ming.__name__
wrapper
這是因為在執(zhí)行裝飾器run的時候闲昭,直接返回的函數(shù)是wrapper罐寨,那么要解決這個問題只需要將func的函數(shù)名賦給wrapper即可,這里Python內置了functools.wraps這個函數(shù)序矩,相當于執(zhí)行了wrapper._name_ = func._name_鸯绿。在裝飾器定義中添加這個@functools.wraps( ),像這樣:
import functools
def run(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s is runing.' % func.__name__)
return func(*args, **kw)
return wrapper
@run
def Ming():
print('I am Ming.')
運行一下Ming.__name__
>>>Ming.__name__
Ming
問題解決簸淀。這樣瓶蝴,上面的一個decorator,裝飾器就是完整的裝飾器了租幕。
? 帶參數(shù)的裝飾器
當然我們還可以給裝飾器加入更大的靈活性舷手,如定義一個帶參數(shù)的裝飾器,這時就需要用三層嵌套的高階函數(shù)了劲绪,如下
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
@run('Executed Successfully.')
def Ming():
print('I am Ming.')
運行結果
>>>Ming()
Ming is runing. Executed Successfully.
I am Ming.
以上就是本節(jié)的全部內容男窟,感謝你的閱讀。
下一節(jié)內容:10.模塊珠叔、包與作用域
有任何問題與想法蝎宇,歡迎評論與吐槽。
和博主一起學習Python吧( ̄▽ ̄)~*