Python带参装饰器的入门练习

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值