本文儘量用簡單代碼附以實例驗證, 逐步理解, 裝飾器原型
**裝飾器背景:** 在不改變原函數代碼, 且保持原函數調用方法不變的情況下, 給原函數增加新的功能 (或者給類增加新屬性和方法)
**核心思想: ** 用一個函數(或者類) 去裝修 一箇舊函數 (或者類) , 造出一個新函數(或者新類)
Python萬物皆對象, 可以隨意賦值使用
def func():
print("第三: ","我是一個函數的輸出結果")
a = 1
print("第一: 我是一個變量 ",a)
a = func #將函數名賦值給變量
print("第二: 我是一個函數名 ",a)
a()
>>>第一: 我是一個變量 1
>>>第二: 我是一個函數名 <function func at 0x000000000220AAE8>
>>>第三: 我是一個函數的輸出結果
變量作用域
# 根據 LEGB原則, 內函數使用外函數的局部變量
def outer():
a = 0
print("我是外部函數的 a:",a)
def inner():
print("我是內部函數打印的 a:",a)
> > > 我是外部函數的 a: 0
> > > 我是內部函數打印的 a: 0
# 引入 nonlocal, 內函數可以修改外函數
def outer():
a = 0
print("我是外部函數的 a:",a)
def inner():
nonlocal a
a += 1
print("我是內部函數加工過的 a:",a)
>>>我是外部函數的 a: 0
>>>我是內部函數加工過的 a: 1
閉包( 延長局部變量生存時間 )
# 內函數 夾帶 外函數的局部變量
def outer():
a = 0
print("我是外部函數打印的 a:",a)
def inner():
print("我是內部函數打印的 a:",a)
return inner
res = outer() # res接收的是一個函數名
print("我是返回出來的內函數名: "res)
res()
>>>我是外部函數打印的 a: 0
>>>我是返回出來的內函數名: <function func at 0x000000000220AAE8>
>>>我是內部函數打印的 a: 0
>>>
# 內函數 夾帶 修改後的外函數的局部變量
def outer():
a = 0
print("我是外部函數打印的 a:",a)
def inner():
nonlocal a
a += 1
print("我是內部函數修改過的 a:",a)
return inner
res = outer() # res接收的是一個函數名
print("我是返回出來的內函數名: "res)
res()
>>>我是外部函數打印的 a: 0
>>>我是返回出來的內函數名: <function func at 0x000000000220CA97>
>>>我是內部函數修改過的 a: 1
函數名可以當做參數傳遞
#新函數對舊函數修飾
def old():
print('我是 舊函數')
def new(f):
print('我是 新函數, 我可以輸出 舊函數')
f()
new( old ) #把舊函數名當做參數, 傳遞給新函數, 在新函數內產生變化
>>>我是 新函數, 我可以輸出 舊函數
>>>我是 舊函數
裝飾器原型
# ### 利用閉包,將函數名當參數傳遞
def outer(f):
def inner():
print("我是外部傳進來的old :",f)
f()
print("我是修飾工,我在舊函數後顯示")
return inner
def old():
print("我是舊函數")
print("我是最初的old: ",old)
print("我是返回出來的內函數inner: ",outer(old)) #outer把函數名old當參數傳回函數內部進行加工, 把函數名inner返回
old = outer(old) #實際上, 舊函數名old已被重新賦值爲inner
print("我不是原來的old了 ",old)
old() #經過改造, 雖然調用結果不變, 但是運行結果已變化
>>>我是最初的old: <function old at 0x00000000021CAA60>
>>>我是返回出來內函數inner: <functionouter<locals>.inner at 0x00000000021CAAE8>
>>>我不是原來的old了 <function outer.<locals>.inner at 0x00000000021CAAE8>
>>>我是外部傳進來的old : <function old at 0x00000000021CAA60>
>>>我是舊函數
>>>我是修飾工,我在舊函數後顯示
裝飾器
# ### 用 @outer 修飾
def outer(f):
def inner():
print("我是外部傳進來的old :",f)
f()
print("我是修飾工,我在舊函數後顯示")
return inner
@outer #實際上,將 old = outer(old) 這句改成 @outer, 放在old函數上方
def old():
print("我是舊函數")
old()
>>>我是外部傳進來的old : <function old at 0x00000000021CAA60>
>>>我是舊函數
>>>我是修飾工,我在舊函數後顯示
裝飾器的嵌套: 下層裝飾器修飾後的函數, 再次被上層裝飾器修飾
def kuozhan(f):
def inner():
print("1我是擴展函數,接收外部傳進來的新old :",f)
f()
print("4我是擴展函數,我在新old函數後顯示")
return inner
def outer(f):
def inner():
print("2我是outer函數,接收外部傳進來的old :",f)
f()
print("3我是outer函數,我在old函數後顯示")
return inner
@kuozhan #old = kuozhan(outer(old))
@outer #old = outer(old)
def old():
print("我是old函數")
old()
>>>1 我是擴展函數,接收外部傳進來的新old : <function outer.<locals>.inner at 0x000000000250AB70>
>>>2 我是外部傳進來的old : <function old at 0x000000000250AAE8>
>>> 我是old函數
>>>3 我是修飾工,我在舊函數後顯示
>>>4 我是擴展函數,我在新old函數後顯示