Python 函數(shù)裝飾器應(yīng)用教程
一、什么是函數(shù)裝飾器
1.函數(shù)裝飾器是Python提供的一種增強(qiáng)函數(shù)功能的標(biāo)記函數(shù);
2.裝飾器是可調(diào)用的函數(shù)對(duì)象,其參數(shù)是另一個(gè)函數(shù)(被裝飾的函數(shù));
我們可以使用修飾器來封裝某個(gè)函數(shù),從而讓程序在執(zhí)行這個(gè)函數(shù)之前與執(zhí)行完這個(gè)函數(shù)之后,分別運(yùn)行某些代碼。這意味著,調(diào)用者傳給函數(shù)的參數(shù)值、函數(shù)返回給調(diào)用者的值,以及函數(shù)拋出的異常,都可以由修飾器訪問并修改。這是個(gè)很有用的機(jī)制,能夠確保用戶以正確的方式使用函數(shù),也能夠用來調(diào)試程序或?qū)崿F(xiàn)函數(shù)注冊(cè)功能,此外還有許多用途。
二、函數(shù)裝飾器的執(zhí)行時(shí)機(jī)
函數(shù)裝飾器在被裝飾函數(shù)編譯解析之后直接執(zhí)行,裝飾器是按照從上到下執(zhí)行的;
函數(shù)裝飾器內(nèi)部定義的返回函數(shù)依附在裝飾器的執(zhí)行環(huán)境中;
函數(shù)裝飾器每次執(zhí)行都會(huì)生成新的返回函數(shù);
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:False
三、變量作用域
Python中將變量聲明和賦值操作合一,很容易導(dǎo)致函數(shù)局部變量覆蓋函數(shù)外的變量
b=6 def f(): print(b) f() # 6
b=6 def f(): print(b) b = 9 f() # UnboundLocalError: local variable 'b' referenced before assignment
通過生成的字節(jié)碼可以看到兩者對(duì)變量b的處理的差異,前者直接LOAD_GLOBAL,后者是LOAD_FAST,但是給b賦值卻在print之后導(dǎo)致報(bào)錯(cuò);
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來強(qiáng)制聲明b是全局變量,然后就可以重新賦值了;
b=6 def f(): global b print(b) b = 9 f() # 6
四、閉包
閉包是是指可以訪問非在函數(shù)體內(nèi)定義的非全局變量的函數(shù);
通過函數(shù)的__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只能使用列表對(duì)象,python提供的nonlocal關(guān)鍵字可以直接使用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
五、保留函數(shù)的元數(shù)據(jù)
函數(shù)裝飾器默認(rèn)會(huì)使用返回的函數(shù)完全取代被裝飾的函數(shù),這樣可能會(huì)導(dǎo)致序列化或者開發(fā)工具智能提示的問題;可以使用functools.wraps來保留原始函數(shù)的標(biāo)準(zhǔn)屬性(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
六、支持關(guān)鍵字參數(shù)、位置參數(shù)
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緩存函數(shù)執(zhí)行結(jié)果
lru_cache內(nèi)部使用函數(shù)的參數(shù)作為key,使用dict進(jìn)行緩存執(zhí)行結(jié)果,減少重復(fù)計(jì)算;
默認(rèn)支持緩存128條記錄,超過后采用LRU方式丟棄多余記錄;
需要注意執(zhí)行中不要改變參數(shù),否則會(huì)影響字典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) # 8
八、使用singledispatch實(shí)現(xiàn)泛型函數(shù)
python不支持方法或者函數(shù)的重載,我們無法單獨(dú)定義不同參數(shù)類型的同名函數(shù);
singledispatch提供了這樣一種能力,其通過注冊(cè)具體的參數(shù)類型和關(guān)聯(lián)的函數(shù);
我們可以在自己的模塊定義自己的類型,并實(shí)現(xiàn)自己的自定義函數(shù);
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
九、通過參數(shù)控制函數(shù)裝飾器的行為
函數(shù)裝飾器本身不支持傳遞參數(shù),解析源代碼的時(shí)候,python會(huì)將被裝飾的函數(shù)對(duì)象作為第一個(gè)參數(shù)傳遞給裝飾器;
我們可以通過在函數(shù)裝飾器外再嵌套一個(gè)函數(shù)工廠來承載裝飾特定函數(shù)的時(shí)候設(shè)置的參數(shù);?
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 函數(shù)裝飾器應(yīng)用教程的詳細(xì)內(nèi)容,更多關(guān)于Python 函數(shù)裝飾器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python與Matlab實(shí)現(xiàn)快速傅里葉變化的區(qū)別
信號(hào)處理免不了要求頻率、畫頻譜圖,但Matlab的fft()函數(shù)與Python的numpy.fft.fft()與scipy.fftpack.fft()函數(shù)得到的是fft變化后的雙邊復(fù)數(shù)值,離畫頻譜圖還有幾句代碼的距離?;驹聿唤榻B了,下面直接懶人投喂,給出Matlab與Python的兩個(gè)函數(shù),直接調(diào)用即可畫頻譜圖2021-10-10python通過shutil實(shí)現(xiàn)快速文件復(fù)制的方法
這篇文章主要介紹了python通過shutil實(shí)現(xiàn)快速文件復(fù)制的方法,涉及Python中shutil模塊的使用技巧,需要的朋友可以參考下2015-03-03python實(shí)現(xiàn)郵件循環(huán)自動(dòng)發(fā)件功能
這篇文章主要介紹了python實(shí)現(xiàn)郵件循環(huán)自動(dòng)發(fā)件功能,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09Python基于gevent實(shí)現(xiàn)高并發(fā)代碼實(shí)例
這篇文章主要介紹了Python基于gevent實(shí)現(xiàn)高并發(fā)代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-05-05pandas中read_sql使用參數(shù)進(jìn)行數(shù)據(jù)查詢的實(shí)現(xiàn)
本文主要介紹了pandas中read_sql使用參數(shù)進(jìn)行數(shù)據(jù)查詢的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06python多進(jìn)程并發(fā)demo實(shí)例解析
這篇文章主要介紹了python多進(jìn)程并發(fā)demo實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12