Python帶參裝飾器

裝飾器(無參)python

  它是一個函數;app

  函數做爲它的形參;ide

  返回值也是一個函數;函數

  能夠使用@functionname方式,簡化調用;spa

裝飾器和高階函數orm

  裝飾器是高階函數,但裝飾器是對傳入函數的功能的裝飾(功能加強)對象

import datetime
import time

def logger(fn):
    def wrap(*args, **kwargs):
        #before 功能加強
        print("args={},kwargs={}".format(args, kwargs))
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        #after 功能加強
        duration = datetime.datetime.now() - start
        print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
        return ret
    return wrap

@logger
def add(x, y):
    print("======call add======")
    time.sleep(2)
    return x + y

print(add(4, y=5))


講一個新的小知識點---文檔字符串
文檔

python的文檔字符串

  python是文檔字符串Documentation Stringsit

  在函數語句塊的第一行,且習慣是多行的文本,因此多使用三引號;

  慣例是首字母大寫,第一行寫概述,空一行,第三行寫詳細描述;

  能夠使用特殊屬性__doc__訪問這個文檔

def add(x, y):
    """This is a function of addition"""

    a = x + y
    return x + y

print("name={}\ndoc={}".format(add.__name__, add.__doc__))
print(help(add))

這就是文檔字符串,經過文檔字符串能夠查看這個函數的幫助等一些信息



咱們在來看一段代碼,它的輸出結果是什麼呢?

import datetime
import time

def logger(fn):
    def wrap(*args, **kwargs):
        """This is a wrapper"""
        #before 功能加強
        print("args={},kwargs={}".format(args, kwargs))
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        #after 功能加強
        duration = datetime.datetime.now() - start
        print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
        return ret
    return wrap

@logger
def add(x, y):
    """This is a function"""
    print("======call add======")
    time.sleep(2)
    return x + y

# print(add(4, y=5))

print(add.__name__, add.__doc__,sep='\n')

運行結果以下:

wrap

This is a wrapper

經過代碼也能看出來,使用裝飾器是有反作用的:

  原函數對象的屬性都被替換了,而使用裝飾器,咱們的需求是查看被封裝函數的屬性,如何解決?

blob.png


想一下,這個函數的調用爲何要寫到79行???插入到其它行,行不行?這個本身考慮想一想吧,這裏就不提了。


既然咱們學會了裝飾器,那如何把copy_properties改形成裝飾器?這就引出了咱們的帶參裝飾器

import datetime
import time

def copy_properties(src):
    def wrapper(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        dst.__qualname__ = src.__qualname__
        return dst
    return wrapper

def logger(fn):
    @copy_properties(fn)# copy_properties.wrapper(logger.wrap),
    def wrap(*args, **kwargs):
        """This is a wrapper"""
        #before 功能加強
        print("args={},kwargs={}".format(args, kwargs))
        start = datetime.datetime.now()
        ret = fn(*args, **kwargs)
        #after 功能加強
        duration = datetime.datetime.now() - start
        print("function {} took {}s.".format(fn.__name__, duration.total_seconds()))
        return ret
    return wrap

@logger
def add(x, y):
    """This is a function"""
    print("======call add======")
    time.sleep(2)
    return x + y

# print(add(4, y=5))

print(add.__name__, add.__doc__, add.__qualname__, sep='\n')

經過copy_properties函數將包裝函數的屬性覆蓋掉包包裝函數;

凡是被裝飾的函數都須要複製這些屬性,這個函數很通用;

能夠將複製屬性的函數構建成裝飾器函數,帶參裝飾器;


需求:獲取函數的執行時長,對時長超過閾值的函數記錄一下:

import datetime
import time

def logger(t):# def logger(t1, t2, t3....tn):
    def _logger(fn):
        #@copy_properties(fn) 能夠把上面寫的複製屬性的函數裝飾在此
        def wrap(*args, **kwargs):
            #before 功能加強
            # print("args={},kwargs={}".format(args, kwargs))
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            #after 功能加強
            duration = (datetime.datetime.now() - start).total_seconds()
            if duration > t:
                print("function {} took {}s.".format(fn.__name__, duration))
            return ret
        return wrap
    return _logger

@logger(3)# add = logger(3)(add), @logger(3, 5, 9,...n)
def add(x, y):
    print("======call add======")
    time.sleep(5)
    return x + y

print(add(4, y=5))

裝飾器(帶參)

  它是一個函數;

  函數做爲它的形參;

  返回值是一個不帶參的裝飾器函數;

  使用@functionname(參數列表)方式調用;

  能夠看作在裝飾器外層又加了一層函數;



將記錄的功能提取出來,這樣就能夠經過外部提供的函數來靈活的控制輸出:

def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
    def _logger(fn):
        @copy_properties(fn):
        def wrapper(*args, **kwargs):
            start = datetime.datetime.now()
            ret = fn(*args, **kwargs)
            delta = (datetime.datetime.now() - start).total_seconds()
            if delta > duration:
                func(fn.__name__, duration)
            return ret
        return wrapper
    return _logger
相關文章
相關標籤/搜索