這篇文章主要介紹Python 函數裝飾器怎么用,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
1.函數裝飾器是Python提供的一種增強函數功能的標記函數;
2.裝飾器是可調用的函數對象,其參數是另一個函數(被裝飾的函數);
我們可以使用修飾器來封裝某個函數,從而讓程序在執行這個函數之前與執行完這個函數之后,分別運行某些代碼。這意味著,調用者傳給函數的參數值、函數返回給調用者的值,以及函數拋出的異常,都可以由修飾器訪問并修改。這是個很有用的機制,能夠確保用戶以正確的方式使用函數,也能夠用來調試程序或實現函數注冊功能,此外還有許多用途。
函數裝飾器在被裝飾函數編譯解析之后直接執行,裝飾器是按照從上到下執行的;
函數裝飾器內部定義的返回函數依附在裝飾器的執行環境中;
函數裝飾器每次執行都會生成新的返回函數;
import sys
def dec1(m):
print(f'{sys._getframe().f_code.co_name} is execute, arg {m.__name__}')
def newm1():
print(f'{sys._getframe().f_code.co_name}')
return newm1;
@dec1
def m1():
print(f'{sys._getframe().f_code.co_name}')
@dec1
def m11():
print(f'{sys._getframe().f_code.co_name}')
if __name__ == '__main__':
print(m1)
print(m11)
print(f'm1==m11:{m1==m11}')
# dec1 is execute, arg m1
# dec1 is execute, arg m11
# <function dec1.<locals>.newm1 at 0x7fdfa97d9160>
# <function dec1.<locals>.newm1 at 0x7fdfa97d91f0>
# m1==m11:FalsePython中將變量聲明和賦值操作合一,很容易導致函數局部變量覆蓋函數外的變量
b=6 def f(): print(b) f() # 6
b=6 def f(): print(b) b = 9 f() # UnboundLocalError: local variable 'b' referenced before assignment
通過生成的字節碼可以看到兩者對變量b的處理的差異,前者直接LOAD_GLOBAL,后者是LOAD_FAST,但是給b賦值卻在print之后導致報錯;
from dis import dis b=6 def f(): print(b) # b = 9 dis(f) # 5 0 LOAD_GLOBAL 0 (print) # 2 LOAD_GLOBAL 1 (b) # 4 CALL_FUNCTION 1 # 6 POP_TOP # 8 LOAD_CONST 0 (None) # 10 RETURN_VALUE
from dis import dis b=6 def f(): print(b) b = 9 # 5 0 LOAD_GLOBAL 0 (print) # 2 LOAD_FAST 0 (b) # 4 CALL_FUNCTION 1 # 6 POP_TOP # 6 8 LOAD_CONST 1 (9) # 10 STORE_FAST 0 (b) # 12 LOAD_CONST 0 (None) # 14 RETURN_VALUE
可以使用global來強制聲明b是全局變量,然后就可以重新賦值了;
b=6 def f(): global b print(b) b = 9 f() # 6
閉包是是指可以訪問非在函數體內定義的非全局變量的函數;
通過函數的__code__及__closure__可以看到局部變量和自由變量及閉包的情況;
def makesum():
sum = [0]
def s(val):
sum[0] += val
return sum[0]
return s
s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)
# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f63321f1b20: list object at 0x7f63321e8a00>,)
# [3]基于三中Python變量作用域的緣故,上邊的sum只能使用列表對象,python提供的nonlocal關鍵字可以直接使用int類型的變量;
def makesum():
sum = 0
def s(val):
nonlocal sum
sum += val
return sum
return s
s = makesum()
print(s(1))
print(s(2))
print(s.__code__.co_varnames)
print(s.__code__.co_freevars)
print(s.__closure__)
print(s.__closure__[0].cell_contents)
# 1
# 3
# ('val',)
# ('sum',)
# (<cell at 0x7f73e6a4ab20: int object at 0x7f73e6b47970>,)
# 3函數裝飾器默認會使用返回的函數完全取代被裝飾的函數,這樣可能會導致序列化或者開發工具智能提示的問題;可以使用functools.wraps來保留原始函數的標準屬性(name、module、__annotations__等);
import functools def dec(func): def real(): '''this is real function''' pass return real def wrapsdec(func): @functools.wraps(func) def real(): '''this is real function''' pass return real @dec def max(nums): '''this is max function''' pass @wrapsdec def sum(nums): '''this is sum function''' print(max) print(max.__name__) print(max.__doc__) print(help(max)) print() print(sum) print(sum.__name__) print(sum.__doc__) print(help(sum)) # <function dec.<locals>.real at 0x7f1bfd4241f0> # real # this is real function # Help on function real in module __main__: # # real() # this is real function # # None # # <function sum at 0x7f1bfd424280> # sum # this is sum function # Help on function sum in module __main__: # # sum(nums) # this is sum function # # None
六、支持關鍵字參數、位置參數
def dec(func): def real(*args, **kwargs): result = func(*args, **kwargs) return result return real @dec def sum(*nums, **kwargs): s = 0 for n in nums: s = s + n for a in kwargs.values(): s = s + a return s print(sum(1,2,3,first=1))
lru_cache內部使用函數的參數作為key,使用dict進行緩存執行結果,減少重復計算;
默認支持緩存128條記錄,超過后采用LRU方式丟棄多余記錄;
需要注意執行中不要改變參數,否則會影響字典key的有效性;
from functools import lru_cache
@lru_cache()
def fibonacci(n):
print(f'fibonacci({n})')
if n<2:
return n
return fibonacci(n-1) + fibonacci(n-2)
print(fibonacci(6))
# fibonacci(6)
# fibonacci(5)
# fibonacci(4)
# fibonacci(3)
# fibonacci(2)
# fibonacci(1)
# fibonacci(0)
# 8python不支持方法或者函數的重載,我們無法單獨定義不同參數類型的同名函數;
singledispatch提供了這樣一種能力,其通過注冊具體的參數類型和關聯的函數;
我們可以在自己的模塊定義自己的類型,并實現自己的自定義函數;
import math import numbers from functools import singledispatch @singledispatch def absall(obj): return abs(obj) @absall.register(numbers.Number) def numabs(num): return abs(num) @absall.register(numbers.Complex) def cabs(c): return math.sqrt(c.real*c.real + c.imag* c.imag) class Line: def __init__(self, startx, starty, endx, endy): self.startx = startx self.starty = starty self.endx = endx self.endy = endy @absall.register(Line) def lineabs(line): y =line.endy - line.starty x = line.endx - line.startx return math.sqrt(x*x + y*y) print(absall(-1.1)) print(absall(3+4j)) l = Line(1,2,4,6) print(absall(l)) # 1.1 # 5.0 # 5.0
函數裝飾器本身不支持傳遞參數,解析源代碼的時候,python會將被裝飾的函數對象作為第一個參數傳遞給裝飾器;
我們可以通過在函數裝飾器外再嵌套一個函數工廠來承載裝飾特定函數的時候設置的參數;
def accesscontrol(checkuser=True, updatesession=True):
def dec(func):
def checkuserf():
print('check user')
return True
def updatesessionf():
print('update session')
return True
def access():
if checkuser:
checkuserf()
if updatesession:
updatesessionf()
func()
return access
return dec
@accesscontrol()
def pushitem():
print('pushitem')
return True
@accesscontrol(checkuser=False)
def getitem():
print('getitem')
return True
# pushitem()
# print()
# getitem()
#
# check user
# update session
# pushitem
#
# update session
# getitem以上是“Python 函數裝飾器怎么用”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。