Python帶參裝飾器的入門路線:終於寫出小白看不懂的代碼了-Wayne大師6

Python帶參裝飾器的入門套路

在學習帶參裝飾器前我們首先複習一下無參裝飾器的寫法:

def add(x, y):
    return x + y


def logger(fn):
    def wrapper(*args, **kwargs):  # 這裏是形參的傳入,可以傳入類型最終由fn|add決定
        print("before the fn/add")
        ret = fn(*args, **kwargs)  # 這裏是實參結構
        print("after the fn/add")
        return ret

    return wrapper


add = logger(add) 
 
add(4, 5)
# ===========output=============>
before the fn/add
after the fn/add





9

帶參裝飾器練習:裝飾器文檔字符串被改變

import time
import datetime

def logger(fn):
    def wrapper(*args, **kwargs):
        """wrapper's document"""
        print("before the fn")
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print("after the fn")
        delta = (datetime.datetime.now() - start).total_seconds()
        print("Funtion{} took {}s.".format(fn.__name__,delta))
        return ret    
    return wrapper
@logger # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """ add's  doc ~~~"""
    time.sleep(1)
    return x+y

print(add.__name__, add.__doc__)
# ==output不是add的document,而是wrapper的document=============>
wrapper wrapper's document

如上,使用裝飾器後add的函數名和文檔都被改變了。如何解決呢?
因爲現在訪問add函數,實際上是在執行wrapper函數,所以使用原來定義的add函數的名稱和文檔屬性,覆蓋wrapper的對應屬性就可以了:

import time
import datetime

def logger(fn):
    def wrapper(*args, **kwargs):

        """wrapper's document"""
        print("before the fn")
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print("after the fn")
        delta = (datetime.datetime.now() - start).total_seconds()
        print("Funtion{} took {}s.".format(fn.__name__,delta))
        return ret    
    
    def copy_properties(src, dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
    
    copy_properties(fn, wrapper)
    
    return wrapper

@logger # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(1)
    return x+y

print(add.__name__, "\n", add.__doc__)
# ==output不是add的document,而是wrapper的document=============>
add 
 add's  doc ~balabala~

寫成函數的方式

import time
import datetime

def logger(fn):
    def wrapper(*args, **kwargs):
        
        """wrapper's document"""
        print("before the fn")
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print("after the fn")
        delta = (datetime.datetime.now() - start).total_seconds()
        print("Funtion{} took {}s.".format(fn.__name__,delta))
        return ret
    
    wrapper.__name__ = fn.__name__  
    wrapper.__doc__ = fn.__doc__
            
    return wrapper
@logger # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """ add's  doc ~~~"""
    time.sleep(1)
    return x+y

print(add.__name__,"\n", add.__doc__)
# ==output不是add的document,而是wrapper的document=============>
add 
  add's  doc ~~~

BTW:

類似copy_properties這種函數往往是公用的,所以定義成全局的比較合適,在調用時候,使用裝飾器調用。

import time
import datetime
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst #這裏注意return的返回值很重要,如果沒有返回值,
                    # 如果沒有返回值就是None。那麼 帶參裝飾器裏的wrapper - > copy_propertiest(fn)-> _copy -> copy(dst) - None
                    #這樣,如果要print(add.__name__)->print(wrapper._name__)..................................print(None.__name__)
    return _copy

def logger(fn):
        
    @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) 
    def wrapper(*args, **kwargs):

        """wrapper's document"""
        print("before the fn")
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print("after the fn")
        delta = (datetime.datetime.now() - start).total_seconds()
        print("Funtion{} took {}s.".format(fn.__name__,delta))
        return ret    
    
#     def copy_properties(src, dst):   # 這是帶參裝飾器的原始狀態
#         dst.__name__ = src.__name__
#         dst.__doc__ = src.__doc__
    
#     copy_properties(fn, wrapper)
    
    return wrapper

@logger # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(1)
    return x+y

print(add.__name__, "\n", add.__doc__)
# ==output不是add的document,而是wrapper的document=============>
add 
 add's  doc ~balabala~

這樣,一個帶參裝飾器就完成了。

import time
import datetime
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
#         return dst #這裏注意return的返回值很重要,如果沒有返回值,
                    # 如果沒有返回值就是None。那麼 帶參裝飾器裏的wrapper - > copy_propertiest(fn)-> _copy -> copy(dst) - None
                    #這樣,如果要print(add.__name__)->print(wrapper._name__)..................................print(None.__name__)
    return _copy

def logger(fn):
        
    @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) 
    def wrapper(*args, **kwargs):

        """wrapper's document"""
        print("before the fn")
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print("after the fn")
        delta = (datetime.datetime.now() - start).total_seconds()
        print("Funtion{} took {}s.".format(fn.__name__,delta))
        return ret    
    
#     def copy_properties(src, dst):   # 這是帶參裝飾器的原始狀態
#         dst.__name__ = src.__name__
#         dst.__doc__ = src.__doc__
    
#     copy_properties(fn, wrapper)
    
    return wrapper

@logger # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(1)
    return x+y

print(add.__name__, "\n", add.__doc__)
# ===============返回值Return因一個返回值的缺失而報錯=============>
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-63-8df557fa2dde> in <module>
     38     return x+y
     39 
---> 40 print(add.__name__, "\n", add.__doc__)
     41 # ===============返回值Return因一個返回值的缺失而報錯=============>


AttributeError: 'NoneType' object has no attribute '__name__'

帶參裝飾器的覆蓋問題

將記錄提取到控制檯,或者記錄到日誌裏面。

import time
import datetime
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst #這裏注意return的返回值很重要,如果沒有返回值,
                    # 如果沒有返回值就是None。那麼 帶參裝飾器裏的wrapper - > copy_propertiest(fn)-> _copy -> copy(dst) - None
                    #這樣,如果要print(add.__name__)->print(wrapper._name__)..................................print(None.__name__)
                    # 另外,如果return不是dst而是src,就會導致新add失效
    return _copy

def logger(fn):
        
    @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) 
    def wrapper(*args, **kwargs):

        """wrapper's document"""
        print("before the fn")
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print("after the fn")
        delta = (datetime.datetime.now() - start).total_seconds()
        print("Funtion{} took {}s.".format(fn.__name__,delta))
        return ret    
    
#     def copy_properties(src, dst):   # 這是帶參裝飾器的原始狀態
#         dst.__name__ = src.__name__
#         dst.__doc__ = src.__doc__
    
#     copy_properties(fn, wrapper)
    
    return wrapper

@logger # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(1)
    return x+y

print(add.__name__, "\n", add.__doc__)
add(4,5)
# ==output不是add的document,而是wrapper的document=============>
add 
 add's  doc ~balabala~
before the fn
after the fn
Funtionadd took 1.001673s.





9

Python帶參裝飾器的應用

檢查函數執行時間。

import time
import datetime
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst #
    return _copy

def logger(fn):
        
    @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) 
    def wrapper(*args, **kwargs):

        """wrapper's document"""
        print("before the fn")
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        print("after the fn")
        delta = (datetime.datetime.now() - start).total_seconds()
        if delta > 3:
            print("Function {} took {}s. It is SLOW.".format(fn.__name__, delta))
        else:
            print("Function {} took {}s. The speed is OK.".format(fn.__name__, delta))
        print("Funtion{} took {}s.".format(fn.__name__,delta))
        return ret    
    

    
    return wrapper

@logger # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(5)
    return x+y

print(add.__name__, "\n", add.__doc__)
add(4,5)
# ==output不是add的document,而是wrapper的document=============>
add 
 add's  doc ~balabala~
before the fn
after the fn
Function add took 5.001807s. It is SLOW.
Funtionadd took 5.001807s.





9

我們可以把delta閾值提出作爲一個參數,這樣就可以自定義了:

import time
import datetime
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst #
    return _copy

def logger(duration = 3):
    def _logger(fn):
        
        @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) 
        def wrapper(*args, **kwargs):

            """wrapper's document"""
            print("before the fn")
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            print("after the fn")
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                print("Function {} took {}s. It is SLOW.".format(fn.__name__, delta))
            else:
                print("Function {} took {}s. The speed is OK.".format(fn.__name__, delta))
            print("Funtion{} took {}s.".format(fn.__name__,delta))
            return ret    
    

        return wrapper
    return _logger

@logger(6) # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(5)
    return x+y

print(add.__name__, "\n", add.__doc__)
add(4,5)
# ==output不是add的document,而是wrapper的document=============>
add 
 add's  doc ~balabala~
before the fn
after the fn
Function add took 5.001199s. The speed is OK.
Funtionadd took 5.001199s.





9

修改爲便於閱讀的三元表達式:

import time
import datetime
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst #
    return _copy

def logger(duration = 3):
    def _logger(fn):
        
        @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) 
        def wrapper(*args, **kwargs):

            """wrapper's document"""
            print("before the fn")
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            print("after the fn")
            delta = (datetime.datetime.now() - start).total_seconds()
#             print("Function {} took {}s. It is .".format(fn.__name__, delta)) if delta > duration else print("Function {} took {}s. The speed is OK.".format(fn.__name__, delta)) 
            print("Function {} took {}s. It is {}.".format(fn.__name__, delta,
                    "slow"if delta > duration else "fast")) 
            return ret    

        return wrapper
    return _logger

@logger(6) # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(5)
    return x+y

print(add.__name__, "\n", add.__doc__)
add(4,5)
# ==output不是add的document,而是wrapper的document=============>
add 
 add's  doc ~balabala~
before the fn
after the fn
Function add took 5.001629s. It is fast.





9

爲了靈活,我們對超出閾值的信息使用一個函數記錄:

import time
import datetime
def copy_properties(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst #
    return _copy

def logger(duration = 3, output = lambda name, delta: print("slow.{} took {}s )".format(name, delta))):
    def _logger(fn):
        
        @copy_properties(fn) #wrapper = copy_properties(fn)(wrapper) 
        def wrapper(*args, **kwargs):

            """wrapper's document"""
            print("before the fn")
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            print("after the fn")
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                output(fn.__name__, delta) #這裏的output指代的是函數一個函數,
            
            return ret    

        return wrapper
    return _logger

@logger(2) # equal to: add = logger(add) # add指向了wrapper
def add(x,y):
    """add's  doc ~balabala~"""
    time.sleep(3)
    return x+y

print(add.__name__, "\n", add.__doc__)
add(4,5)
# ==output不是add的document,而是wrapper的document=============>
add 
 add's  doc ~balabala~
before the fn
after the fn
slow.add took 3.000753s )





9
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章