Python:函数 ----》装饰器函数

目录

引入

 装饰器【无参】

文档字符串 

带参装饰器


 

引入

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

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

用嵌套函数、柯里化来解决

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

def logger(fn):
    def _loger(*args,**kwargs):
        print("函数开始运行")
        ret = fn(*args,**kwargs)
        print("函数结束")
        return ret
    return _loger

print(logger(add)(10,40))

在变形【这就是装饰器函数,侵入式代码】

  • 装饰器语法糖

def logger(fn):
    def _loger(*args,**kwargs):
        print("函数开始运行")
        ret = fn(*args,**kwargs)
        print("函数结束")
        return ret
    return _loger

@logger    #等价于 add1 = logger(add1)  
def add1(x,y): 
    return x + y

#add1 = logger(add1) 
print(add1(5,10))

 

 装饰器【无参】

装饰器(无参)

  • 它是一个函数
  • 函数作为它的形参
  • 返回值也是一个函数
  • 可以使用@functionname方式,简化调用方式

装饰器和高阶函数

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

文档字符串 

Python的文档

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

只能放在番薯的第一行,否则无法获取到,显示为None
 

def add(x,y):
    """
    This a funation add
    :param x:
    :param y:
    :return:
    """
    return x + y
print("name={},\ndoc={}".format(add.__name__,add.__doc__))


name=add,
doc=
    This a funation add
    :param x:
    :param y:
    :return:

怎样才能使用,@logger装饰器的函数之后,显示add原函数的doc文档信息??

 


def logger(fn):
    def _loger(*args,**kwargs):
        "This funation logger"
        print("函数开始运行")
        ret = fn(*args,**kwargs)
        print("函数结束")
        return ret
    return _loger
@logger
def add(x,y):
    '''
    This is funation add
    '''
    return x + y

print(add.__name__,add.__doc__)

结果:
_loger This funation logger

变法一:自己编写函数去实现

  • 通过copy_properties函数将被包装函数的属性覆盖掉包装函数
  • 凡是被装饰的函数都需要复制这些属性,这个函数很通用
  • 可以将复制属性的函数构建成装饰器函数,带参装饰器
def copy_doc(src,dst):
    dst.__name__ = src.__name__
    dst.__doc__ = src.__doc__

def logger(fn):
    def loger(*args,**kwargs):
        "This funation logger"
        print("函数开始运行")
        ret = fn(*args,**kwargs)
        print("函数结束")
        return ret
    copy_doc(fn,loger)
    return loger
@logger
def add(x,y):
    '''
    This is funation add
    '''
    return x + y

print(add(3,6),add.__name__,add.__doc__)


结果:
函数开始运行
函数结束
9 add 
    This is funation add

变法二:进一步改变为装饰器函数

def copy_doc(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst
    return _copy

def logger(fn):

    @copy_doc(fn)   #copy_doc(fn,loger)
    def loger(*args,**kwargs):
        "This funation logger"
        print("函数开始运行")
        ret = fn(*args,**kwargs)
        print("函数结束")
        return ret
    return loger

@logger
def add(x,y):
    '''
    This is funation add
    '''
    return x + y

print(add(3,6),add.__name__,add.__doc__)

结果:
函数开始运行
函数结束
9 add 
    This is funation add
    

 

带参装饰器

带参装饰器

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

例子:计算函数运行行多长时间

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).total_seconds()
        print("function {} took {}s.".format(fn.__name__, duration))
        return ret
    return wrap

@logger # 相当于 add = logger(add)
def add(x, y):
    print("===call add===========")
    time.sleep(2)
    return x + y
print(add(4, y=7))

结果:
args=(4,), kwargs={'y': 7}
===call add===========
function add took 2.007478s.
11

上面的例子,不够灵活,如果我们计算,大于3秒,小于5秒的函数,改怎么来改

  • 时间自己来控制
import datetime
import time
def logger(t1,t2):
    def t_time(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 >t1 and duration < t2:
                print("function {} took {}s.".format(fn.__name__, duration))
            return ret
        return wrap
    return t_time

@logger(3,5) # 相当于 add = logger(add)
def add(x, y):
    print("===call add===========")
    time.sleep(4)
    return x + y
print(add(4, y=7))

结果:
args=(4,), kwargs={'y': 7}
===call add===========
function add took 4.002905s.
11

总结
最难的装饰器也就是上面个这个样子了 

将记录的功能提取出来,这样就可以通过外部提供的函数来灵活的控制输出

import datetime

def copy_doc(src):
    def _copy(dst):
        dst.__name__ = src.__name__
        dst.__doc__ = src.__doc__
        return dst
    return _copy

def logger(duration, func=lambda name, duration: print('{} took {}s'.format(name, duration))):
    def _logger(fn):
        @copy_doc(fn) # wrapper = wrapper(fn)(wrapper)
        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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值