python学习笔记3.4-函数装饰器

软件开发的过程中,最基本的技能就是:不要重复自己的工作。也就是说,在任何时候,当需要创建高度重复的代码时,通常都需要寻找一个更加快捷的解决方案。在python中,这类问题常常会归为“元编程”。 简而言之,元编程的主要目标是创建函数和类,并用他们来操纵代码(通常的行为有生成、修改、包装已有的代码)。Python中基于这个目的的方法有装饰器、类装饰器、元类以及有用的主题(常见的有对象签名、用exec()来执行代码以及检查函数和类的内部结构)。 元编程系列主要内容是探讨各种元编程技术,通过实例来讲解如何利用这些技术来自定义python的行为,使其能满足我们不同寻常的需求。

1 装饰器

1.1 用装饰器给函数添加一个包装

装饰器的本质就是一个函数,它可以接受一个函数作为输入并返回一个新的函数作为输出。我们可以利用给函数来建立一个装饰器(相当于给函数加上包装层)来添加额外的处理,例如记录日志、计时统计等。 最简单的一个例子是计时系统,我想记录函数的运行时间并打印到控制台,最通常的做法是:

def CutDown(n):
    import time
    start = time.time()

    while n > 0:
        n -= 1

    end = time.time()

    print(end-start)

CutDown(10000000)
屏幕输出:1.1250569820404053

下面引入装饰器方法:

def GetRunTime(func):
    import time
    from functools import wraps

    @wraps(func)
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end - start)
        return result

    return wrapper


@GetRunTime
def cutdown(n):
    while n > 0:
        n -= 1

cutdown(10000000)
屏幕输出:cutdown 1.1274616718292236

用第二种方法的好处就是,加入我需要在很多的函数上记录其运行时间,则只需要在需要记录时间的函数上面添加@GetRunTime即可,调试非常方便,代码也简洁易懂。

装饰器内部的代码一般会涉及创建一个新的函数,利用*args和**kwargs来接受任意的参数,在示例中wrapper()函数就是这么操作的。在这个函数的内部,我们需要调用原来的输入参数(即被包装的函数的输入参数)并返回它的结果。

其实运行过程就是将被装饰函数打包到装饰器中运行并返回运行结果,作为代价肯定是需要牺牲一定的效率的。在示例中还有一个是需要注意的,那就是@wraps(func),它用来保存函数的元数据。其实也可以不要这个,但是就会丢失被装饰函数的一些元数据,例如函数名、文档字符串、函数注解以及调用签名。1.2就会讲解如何保存元数据。

1.2 运用装饰器时保存函数的元数据

在1.1中也提到过,即使没有@wraps(func)函数也能运行,代价是丢失一些函数的元数据:

def GetRunTime(func):
    import time
    #from functools import wraps

   # @wraps(func)
    def wrapper(*args, **kwargs):
        """
        :param args:
        :param kwargs:
        :return:
        """
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(func.__name__, end - start)
        return result

    return wrapper


@GetRunTime
def cutdown(n):
    """
    :param n:
    :return:
    """
    while n > 0:
        n -= 1

cutdown(10000000)
print(cutdown.__name__)
print(cutdown.__doc__)

控制台打印信息:

cutdown 1.4844520092010498
wrapper

        :param args:
        :param kwargs:
        :return:

我们发现打印信息不是cundown这个函数的,而是装饰器函数的。再使用@wraps(func)这个技术: (代码略) 控制台打印信息:

cutdown 1.193265676498413
cutdown

    :param n:
:return:

这个时候所有信息都属于cutdown函数的。 上面这个例子告诉我们,在我们自己编写装饰器的时候一定要记得使用@wraps(func),这样才不会丢失被装饰函数的元数据。同时,从这个例子中我们也可以看到被装饰函数的运行过程,装饰器会接管被装饰函数的所有,并返回结果。 @装饰器的重要特性就是它可以通过wrapped属性来访问被包装的函数,所以我们可以利用这个特性来解包。

1.3 对装饰器进行解包

在1.2的末尾也提到过,利用wrapped属性来进行解包。例如在1.2中可以直接使用:

@GetRunTime
def cutdown(n):
    """
    :param n:
    :return:
    """
    print('cundown is running')
    while n > 0:
        n -= 1

cutdown.__wrapped__(10000000)
打印输出:
cundown is running

当有多个装饰器的函数也可以通过这种方法来访问。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏静默虚空的博客

[Java 进阶]异常

异常:程序在运行过程中发生由于硬件设备问题、软件设计错误等导致的程序异常事件。 世上没有百分之百完美的程序。程序总难免存在各式各样的问题。所以,程序中添加对于错...

1878
来自专栏守望轩

c#的细节(二)-修饰符

写在最前面的话: 《c#的细节》是我当初学习c#时候,做的一些学习笔记的整理,现在回头看来这些都是非常浅显的知识,是c#非常细节的地方,同时也是不能忽略的...

1717
来自专栏Coco的专栏

高性能Javascript--高效的数据访问

832
来自专栏企鹅号快讯

Web前端知识体系精简——CSS 篇

css是用来对html进行修饰的一门语言。 1、选择器 css的选择器有很多种,常用的有类选择器、标签选择器、ID选择器、后代选择器、群组选择器、伪类选择器(b...

1878
来自专栏IMWeb前端团队

CSS Modules使用详解

CSS Modules CSS 模块化 不管是用jquery还是react开发,都会遇到的一系列 CSS 的问题: 全局污染 命名混乱 依赖引入复杂 无法共享变...

1945
来自专栏Bingo的深度学习杂货店

清除浮动的方法

本章主要介绍三种常用的清除浮动的方法,主要包括: ---- [1] 增加一个空 div, 使用 clear:both 将浮动元素 "挤到" 父元素中 [2] ...

2525
来自专栏Python疯子

TebsorFlow基本语法

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invi...

632
来自专栏柠檬先生

html5 新特性

1.querySelector 返回文档中匹配指定css选择器的一个元素.     注意:uerySelector() 方法仅仅返回匹配指定选择器的第一个元...

20910
来自专栏农夫安全

【weakfilescan】敏感文件扫描工具

weakfilescan 基于爬虫,动态收集扫描目标相关信息后进行二次整理形成字典规则,利用动态规则的多线程敏感信息泄露检测工具,支持多种个性化定制选项,包括...

3638
来自专栏Crossin的编程教室

​Python 3 新特性:类型注解

我们知道 Python 是一种动态语言,变量以及函数的参数是不区分类型。因此我们定义函数只需要这样写就可以了:

1212

扫码关注云+社区