文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python带参装饰器

2023-01-31 01:52

关注

装饰器(无参)

  它是一个函数;

  函数作为它的形参;

  返回值也是一个函数;

  可以使用@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
        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 Strings

  在函数语句块的第一行,且习惯是多行的文本,所以多使用三引号;

  惯例是首字母大写,第一行写概述,空一行,第三行写详细描述;

  可以使用特殊属性__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


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯