Python装饰器

装饰器

需求:
    一个加法函数,想增强它的功能,能够输出被调用过以及调用的参数信息

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

增加信息输出功能:

def add(x,y):
    print('call add,x+y') #日志输出到控制台
    return x+y

上面的加法函数是完成了需求,但是有一下的缺点:
    打印是一个功能,这条语句和add函数耦合太高
    加法函数属于业务功能,而输出信息的功能,属于非业务功能代码,不该放在业务函数add中

下面代码做到了业务功能分离,但是fn函数调用传参是个问题:

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

def logger(fn):
    print('begin') #增强的输出
    x = fn(4,5)
    print('end')
    return x

print(logger(add))

解决了传参的问题,进一步改变

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

def logger(fn,*args,**kwargs):
    print('begin')
    x = fn(*args,**kwargs)
    print('end')
    return x

print(logger(add,5,y=60))

柯里化:

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

def logger(fn):
    def wrapper(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

add = logger(add)
print(add(x=5,y=10))

装饰器语法糖:

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

def logger(fn):
    def wrapper(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

装饰器和高阶函数
    装饰器可以是高阶函数,但装饰器是对传入函数的功能的装饰(功能增强)

def logger(fn):
    def wrapper(*args,**kwargs):
        print("args={},kwargs={}".format(args,kwargs))
        start = datetime.datetime.now()
        ret = fn(*args,**kwargs)
        duration = datetime.datetime.now() - start
        print('function{},took{}s.'.format(fn.__name__,duration.total_seconds()))
        return ret
    return wrapper
@logger
def add(x,y):
    print('====')
    time.sleep(2)
    return x+y
print(add(4,7))

装饰器相当于在不修改当前代码的情况下,增加原有代码的功能

Python的文档
    Python文档字符串Documentation Strings
    在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号
    惯例是首字母大写,第一行写概述,空一行,第三行写详细描述
    可以使用特殊属性_doc_访问这个文档

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

add.__doc__
'This is a function of addition'

help(add)
Help on function add in module __main__:

add(x, y)
    This is a function of addition
def logger(fn):
    def wrapper(*args,**kwargs):
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

@logger
def add(x,y):
    """This is a function of addition"""
    return x + y

print("name = {},doc = {}".format(add.__name__,add.__doc__))
name = wrapper,doc = None

提供一个函数,被封装函数属性 copy>包装函数属性

def copy_properties(src,dst):
    dst._name_ = src.__name__
    dst.__doc__ = src.__doc__
    
def logger(fn):
    def wrapper(*args,**kwargs):
        'I am wrapper'
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    copy_properties(fn,wrapper)
    return wrapper

@logger
def add(x,y):
    """This is a function of addition"""
    return x + y

print("name = {},doc = {}".format(add.__name__,add.__doc__))
name = wrapper,doc = This is a function of addition

通过copy_properties函数将被包装函数的属性覆盖掉包装函数
凡是被装饰的函数都需要复制这些属性,这个函数很通用
可以将复制属性的函数构建成装饰函数,带参装饰器

提供一个函数,被封装函数属性 copy>包装函数属性,改成带参装饰器

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)
    def wrapper(*args,**kwargs):
        'I am wrapper'
        print('begin')
        x = fn(*args,**kwargs)
        print('end')
        return x
    return wrapper

@logger
def add(x,y):
    """This is a function of addition"""
    return x + y

print("name = {},doc = {}".format(add.__name__,add.__doc__))
name = wrapper,doc = This is a function of addition

需求:获取函数的执行时长,对时长超过阈值的函数记录一下

from datetime import timedelta
import datetime
def logger(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()
            print('so slow')  if delta > duration else print('so fast')
            return ret
        return wrapper
    return _logger

@logger(5)   #add = logger(5)(add) = _logger(add)  = wrapper(*args,**kwargs)
def add(x,y):  
    time.sleep(3)
    return x + y
print(add(5,6))

so fast
11

带参装饰器
    它是一个函数
    函数作为它的形参
    返回值是一个不带参的装饰器函数
    使用@functionname(参数列表)方式调用
    可以看做在装饰器外层又加上了一层函数,这个函数可以多参数

from datetime import timedelta
import datetime
def logger(duration,func=lambda name,delta:print('{} took {:.2f}s'.format(name,delta))):
    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()
            print(func)
            if delta > duration:
                func(fn.__name__ ,delta)  
            return ret
        return wrapper
    return _logger

@logger(2)   #add = logger(5)(add) = _logger(add)  = wrapper(*args,**kwargs)
def add(x,y):  
    time.sleep(3)
    return x + y
print(add(5,6))

functools模块

functools.update_wrapper(wrapper,wrapped,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)

    类似copy_properties功能
    wrapper包装函数、被更新者,wrapped被包装函数、数据源
    元祖WRAPPER_ASSIGNMENTS中是要被覆盖的属性 ‘module’,‘name’,‘qualname’,‘doc’,'annotations’模块名、名称、限定名、文档、参数注解
    元祖WRAPPER_UPDATES中是要被更新的属性,_dict_属性字典
    增加一个_wrapped_属性,保留着wrapped函数

@functools.wraps(wrapped,assigned = WRAPPER_ASSIGNMENTS,updated=WRAPPER_UPDATES)
    类似copy_properties功能
    wrapped被包装函数
    元组WRAPPER_ASSIGNMENTS中是要被覆盖的属性
module’,‘name’,‘qualname’,‘doc’,'annotations’模块名、名称、限定名、文档、参数注解
    元祖WRAPPER_UPDATES中是要被更新的属性,_dict_属性字典
    增加一个_wrapped_属性,保留着wrapped函数

from datetime import timedelta
import datetime
import functools
def logger(duration,func=lambda name,delta:print('{} took {:.2f}s'.format(name,delta))):
    def _logger(fn):
        @functools.wraps(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__ ,delta)  
            return ret
        return wrapper
    return _logger

@logger(2)   #add = logger(5)(add) = _logger(add)  = wrapper(*args,**kwargs)
def add(x,y):  
    time.sleep(3)
    return x + y
print(add(5,6))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值