一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Python裝飾器詳解

 網摘文苑 2023-02-01 發(fā)布于新疆

裝飾器的作用與原理

相當于java中的注解

我們可以在不入侵一個函數的情況下,在這個函數前和函數后,執(zhí)行一些預設的功能。通常我們會把函數中都會用到的某一類功能(如:鑒權、日志、參數校驗等)封裝成一個裝飾器,這樣代碼更簡潔,語義也更清晰。

在python中用@ 裝飾器函數,簡化了上述設計模式的使用過程

我們來看一個簡單應用

def log(fn): def wrap_function(): print('準備運行函數fn') fn() print('函數fn運行結束') return wrap_function@logdef fnA(): print('正在運行fnA....')fnA()# 輸出:'''準備運行函數fn正在運行fnA....函數fn運行結束'''

與下面的代碼是等價的

def log(fn):    def wrap_function():		    print('準備運行函數fn')		    fn()		    print('函數fn運行結束')		return wrap_functiondef fnA():    print('正在運行fnA....')b = log(fnA)b()


1. log() 作為裝飾器函數,封裝了通用的業(yè)務邏輯,去裝飾 fnA()
2. @log 相當于簡化了裝飾器函數的賦值步驟:log(fnA),代碼閱讀時的業(yè)務邏輯更簡潔。

使用擴展

  • 在裝飾器函數中 return wrap_function,返回是必須的嗎? 從上述的代碼中,我們看到:裝飾器函數要被賦值給一個變量,并最終被執(zhí)行的。所以裝飾器函數需要返回一個可調用對象callable。所以不返回,返回整數字符串等,程序都是會報錯的。 可以返回一個不相干的函數,程序不會報錯。當然這樣不推薦。
  • 在裝飾函數中,如何調用原函數的入參?
def log(fn): def wrap_function(a): print('原函數入參...', a) print('準備運行函數fn') fn(a) print('函數fn運行結束') return wrap_function @log def fnA(a): print('正在運行fnA...', a) fnA('入參A') # 輸出: ''' 原函數入參... 入參A 準備運行函數fn 正在運行fnA... 入參A 函數fn運行結束 '''
  • 接受任意數量入參
def log(fn): 	def wrap_function(*args,**kwargs):   	print('原函數入參...', args) 		print('準備運行函數fn')		fn(*args,**kwargs) 		print('函數fn運行結束') 	return wrap_function @logdef fnA(a, b): 	print('正在運行fnA...', a, b) fnA('入參A', '入參B') # 輸出: ''' 原函數入參... ('入參A', '入參B') 準備運行函數fn 正在運行fnA... 入參A 入參B 函數fn運行結束 '''

函數裝飾器嵌套執(zhí)行順序

先由上到下執(zhí)行before,執(zhí)行完原函數后,再由下往上執(zhí)行After函數

def deA(fn): def wrap_function(*args, **kwargs): print('deA原函數入參...', args) print('deA準備運行函數fn') fn(*args, **kwargs) print('deA函數fn運行結束') return wrap_functiondef deB(fn): def wrap_function(*args, **kwargs): print('deB原函數入參...', args) print('deB準備運行函數fn') fn(*args, **kwargs) print('deB函數fn運行結束') return wrap_functiondef deC(fn): def wrap_function(*args, **kwargs): print('deC原函數入參...', args) print('deC準備運行函數fn') fn(*args, **kwargs) print('deC函數fn運行結束') return wrap_function@deA@deB@deCdef fnA(a, b): print('正在運行fnA...', a, b)fnA('入參A', '入參B')# 輸出:'''deA原函數入參... ('入參A', '入參B')deA準備運行函數fndeB原函數入參... ('入參A', '入參B')deB準備運行函數fndeC原函數入參... ('入參A', '入參B')deC準備運行函數fn正在運行fnA... 入參A 入參BdeC函數fn運行結束deB函數fn運行結束deA函數fn運行結束'''

帶參數的裝飾器

在上述基本的裝飾器的基礎上,在外面套一層接收參數的函數,來實現裝飾器帶參數功能

def logger(msg=None):	  def out_wrapper(fn):	      def wrap_function(*args, **kwargs):	          print('裝飾器的參數:', msg)	          print('準備運行函數fn')	          fn(*args, **kwargs)	          print('函數fn運行結束')	      return wrap_function	  return out_wrapper@logger(msg='hi')def fnA(a):		print('正在運行fnA....', a)fnA('入參A')# 輸出:'''裝飾器的參數: hi準備運行函數fn正在運行fnA.... 入參A函數fn運行結束'''

Python常用的內置裝飾器

@staticmethod、 @classmethod、 @property、@abstractmethod

  • 靜態(tài)方法 @staticmethod
    將函數轉成靜態(tài)方法
class B: @staticmethod def add(a, b): return a + b B.add(10, 5) # 15
  • 類方法 @classmethod
    將函數轉成類方法
class B:   @classmethod 	def add(cls):   	print('This is class method')B.add() # This is class method
  • 屬性 @property
    將函數轉成只讀屬性,可以防止被外部修改
class B: def __init__(self): self._var1 = 3 self.var2 = 5 @property def var1(self): return self._var1b = B() b.var1 b.var1 = 10 # 嘗試給其賦值,會報錯
  • 抽象方法 @abstractmethod
    實現抽象類的特性,含有@abstractmethod修飾的父類不能實例化,子類必須實現所有被@abstractmethod裝飾的方法
from abc import ABC, abstractmethod class F(ABC):   @abstractmethod 	def info(self):   	print('info') 	@abstractmethod 	def f(self):   	print('f') class S(F): 	def info(self):   	print('S - info') f = F() # 報錯:父類不能被實例化 s = S() # 報錯,子類沒有實現父類所有抽象方法

@wraps()語法糖

由于Python的裝飾器是通過閉包實現的,所以在裝飾函數中,獲取導的屬性并非原函數,這就不是很完美

def logger(fn): def wrap_function(*args, **kwargs): ''' 這是裝飾器函數 ''' print('裝飾器中。。。。') return fn(*args, **kwargs) return wrap_function@loggerdef fnA(): ''' fnA中的打印 ''' print('正在運行fnA....')print(fnA.__name__, fnA.__doc__)# 輸出:'''wrap_function 這是裝飾器函數'''

加上@wraps() 后,修飾內部函數,保證這個內部函數帶有傳進來這個函數的屬性

from functools import wraps    def logger(fn):    @wraps(fn)    def wrap_function(*args, **kwargs):        '''        這是裝飾器函數        '''        print('裝飾器中。。。。')        return fn(*args, **kwargs)    return wrap_function@loggerdef fnA():    '''    fnA中的打印    '''    print('正在運行fnA....')print(fnA.__name__, fnA.__doc__)# 輸出:'''fnA         fnA中的打印'''

類裝飾器

通過 __call__ 將類變成調用對象

class Logger: def __init__(self, func): self.func = func def print_console(self): print('console print') def __call__(self, *args, **kwargs): self.print_console() self.func()@Loggerdef fnA(): print('fnA正在運行。。。')fnA()# 輸出'''console printfnA正在運行。。。'''

帶參數的類裝飾器

class Logger:    def __init__(self, prefix):        self.prefix = prefix    def print_console(self):        print('console print', self.prefix)    def notify(self):        self.print_console()    def __call__(self, func):        def wrapper(*args, ** kwargs):            print(self.prefix, 'wrapper before...')						self.notify()            ret = func(*args, ** kwargs)            print(self.prefix, 'wrapper after...')            return ret        return wrapper@Logger(prefix='Logger')def fnA():    print('fnA正在運行。。。')fnA()# 輸出'''Logger wrapper before...console print LoggerfnA正在運行。。。Logger wrapper after...'''

有了類裝飾器,就可以使用繼承了

from functools import wrapsclass Logger: def __init__(self, prefix='logger'): self.prefix = prefix def print_console(self): print('console print', self.prefix) def notify(self): self.print_console() def __call__(self, func): @wraps(func) def wrapper(*args, **kwargs): print(self.prefix, 'wrapper before...') self.notify() ret = func(*args, **kwargs) print(self.prefix, 'wrapper after...') return ret return wrapperclass EmailLogger(Logger): def __init__(self, prefix='Email', email='abc@test.com'): Logger.__init__(self, prefix) self.email = email def print_email(self): print('email print:', self.email) def notify(self): self.print_email()@EmailLogger(email='test@test.com')def fnA(): print('fnA正在運行。。。')fnA()# 輸出:'''Email wrapper before...email print: test@test.comfnA正在運行。。。Email wrapper after...'''

    本站是提供個人知識管理的網絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯系方式、誘導購買等信息,謹防詐騙。如發(fā)現有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    国产成人精品一区二区三区| 日韩精品一级片免费看| 我想看亚洲一级黄色录像| 久久亚洲精品成人国产| 欧美日韩亚洲精品在线观看| 日本三区不卡高清更新二区| 精品国产亚洲区久久露脸| 国产日韩欧美在线播放| 日韩精品视频免费观看| 日本 一区二区 在线| 精品亚洲一区二区三区w竹菊| 97人妻人人揉人人躁人人| 国产免费无遮挡精品视频 | 免费国产成人性生活生活片| 少妇人妻无一区二区三区| 国产又色又爽又黄又免费| 欧美一区日韩二区亚洲三区| 麻豆一区二区三区精品视频| 不卡中文字幕在线免费看| 久久亚洲午夜精品毛片| 欧美日韩国产精品黄片| 五月婷婷六月丁香亚洲| 久久国产亚洲精品赲碰热| 久久精品蜜桃一区二区av| 亚洲精品小视频在线观看| 亚洲中文字幕三区四区| 国产欧美日韩精品一区二| 欧美日韩国产福利在线观看| 人妻少妇久久中文字幕久久| 精品精品国产欧美在线| 人妻内射精品一区二区| 香蕉网尹人综合在线观看| 高清不卡视频在线观看| 国产精品午夜视频免费观看| 精品香蕉国产一区二区三区| 日韩人妻中文字幕精品| av免费视屏在线观看| 日本在线不卡高清欧美| 亚洲中文字幕高清视频在线观看| 国产麻豆精品福利在线| 亚洲中文字幕高清视频在线观看|