专栏首页从流域到海域Python 装饰器(Decorator)

Python 装饰器(Decorator)

引入

如果你学过Java的UML设计模式,那么你一定对Decorator Pattern和你熟悉,Decorator Pattern即装饰器模式(也译修饰器模式),是著名的四人帮(Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides. 设计模式:可复用面向对象软件的基础. 北京: 机械工业出版社)书中介绍的23种设计模式之一。

In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class.[1] The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.[2] The decorator pattern is structurally identical to the chain of responsibility pattern, the difference being that in a chain of responsibility, exactly one of the classes handles the request, while for the decorator, all classes handle the request.

提示:在面向对象的概念中,行为是抽象的说法,体现在代码上的就是方法(函数)。 简述:在面向对象编程中,装饰器模式是一种允许动态或者静态向一个对象添加新的行为而不会影响到其他对象的一种设计模式。

Python 装饰器(Decorator)

Python includes a more natural way of decorating a function by using an annotation on the function that is decorated. https://en.wikipedia.org/wiki/Decorator_pattern 译:Python提供了一种更为自然的方式来装饰一个函数,这种方法通过在被装饰的函数前加一个注释来时实现。

Python提供了装饰器(Decorator)来更为简便的实现Java的装饰模式(Decorator Pattern),其不需要在类的层面就可以实现对一个函数进行装饰,你可以将装饰理解为添加新的功能。 装饰器充分体现了Python的简洁性,因为在Java是一种设计模式,而在Python这里就只是简单的一句注释。

#下面是来自维基百科的一个例子
def benchmark(func):    #定义了一个装饰器 
    """
    Print the seconds that a function takes to execute.
    """
    from time import time
    def wrapper(*args, **kwargs):
        t0 = time()
        res = func(*args, **kwargs)
        print("function @{0} took {1:0.3f} seconds".format(func.__name__, time() - t0))
        return res
    return wrapper

@benchmark              #声明使用装饰器
def wait_some_seconds(num_seconds = 1):
    from time import sleep
    sleep(num_seconds)

wait_some_seconds(1.11)
# function @wait_some_seconds took 1.11 seconds
wait_some_seconds(5)
# function @wait_some_seconds took 5.000 seconds
#结果
function @wait_some_seconds took 1.111 seconds
function @wait_some_seconds took 5.001 seconds

如何来理解: 一个装饰器就是一个以被装饰函数为参数,以wrapper函数为闭包,返回值为wrapper函数的一个函数,其特点是wrapper采用了(*args, **kwargs)即万用参数表作为参数,可以接收任何类型的参数,使用时在被装饰函数前加上@装饰器名作为使用装饰器的声明。 被装饰的函数依然采用自己的函数名进行调用,但是一旦一个函数前有@装饰器名这个注释,就表示其添加了装饰器中wrapper函数所具有的功能,相当于装饰器函数(被装饰函数)即装饰器函数调用了被装饰的函数,被装饰函数作为参数传进去,所以装饰器函数可以使用被装饰函数的功能,同时又可以使用自身的功能,从而实现装饰效果。

#我们在上面的代码中加入这一条语句:__name__是python所有函数都自动封装的一个对象的属性 即函数的名字
print(wait_some_seconds.__name__)
#结果
wrapper

#这个结果也说明了Python中装饰器的实现正如上面作者个人的理解

但是这并不是我们所期望的,因为所有被装饰的函数的名字都是wrapper,我们只是想给函数添加新功能,而不想做多余的更改。 Python中封装了functools.wraps()这个方法来实现对被装饰函数的__name__属性进行重命名,它其实也是一个装饰器,这个装饰器包含的功能是将被装饰函数的名字赋值给装饰器的wrapper的__name__属性,从而实现了被装饰函数名字的正常显示,虽然这种正常显示是人为进行更改的。

#完全体装饰器 对维基上的Pyhton装饰器例子进行了更改
import functools             #@functools.wraps()方法位置
def benchmark(func):         #定义了一个装饰器 
    """
    Print the seconds that a function takes to execute.
    """
    from time import time
    @functools.wraps(func)   #声明使用functools.wraps()装饰器
    def wrapper(*args, **kwargs):
        t0 = time()
        res = func(*args, **kwargs)
        print("function @{0} took {1:0.3f} seconds".format(func.__name__, time() - t0))
        return res
    return wrapper

@benchmark              #声明使用装饰器
def wait_some_seconds(num_seconds = 1):
    from time import sleep
    sleep(num_seconds)

wait_some_seconds(1.11)
# function @wait_some_seconds took 1.11 seconds
wait_some_seconds(5)
# function @wait_some_seconds took 5.000 seconds
print(wait_some_seconds.__name__)

#结果
function @wait_some_seconds took 1.111 seconds
function @wait_some_seconds took 5.000 seconds
wait_some_seconds

装饰器也可以自带参数,为了把这个参数传进去,我们需要在再加一层函数嵌套,把该参数错作为最外层函数的参数。

import functools

def sample(msg):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print('%s函数名:%s():' % (msg, func.__name__))
            return func(*args, **kw)
        return wrapper
    return decorator

@sample('this is a sample:\n')
def hello():
    print('Hello, World')

hello()

#结果
this is a sample:    #换行得以正确实现是因为%s格式化输出字符串 正确识别了换行符
函数名:hello():
Hello, World

参考资料(这篇廖神其实写的有点难以理解): https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014318435599930270c0381a3b44db991cd6d858064ac0000

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Python 迭代(iteration)

    对一组数据进行遍历访问称为迭代(iteration)。 迭代是Python高级特性之一,而且Python的迭代比其他语言更为简便。但是请注意,迭代操作...

    Steve Wang
  • 在Win10上是用Anaconda搭建TensorFlow开发环境

    以下内容原本是作为毕业设计的一部分的,因此绝对认真和详细,由于内容过多所以被删减了,就当福利送给大家了。 2.2 在Windows 10上搭建TensorFl...

    Steve Wang
  • 《笨办法学Python》 第38课手记

    《笨办法学Python》 第38课手记 注意这是第三版的《笨办法学Python》的内容,我后来发现第三版存在很大的问题,就放弃了第三版开始使用第四版,第四版的第...

    Steve Wang
  • 解读小程序权限,谈谈817产品功能迭代

    话说,微信小程序最近变动频繁,两天发布了两次更新,相当有诚意。 不少同学问为啥花叔还没更新文章,主要原因是最近有点忙就搁置了,不过没关系,趁周末有空给大家解读一...

    花叔
  • PHP5.4新特性trait

    php //代码片段1 class Hello { public $id = 2; public function __constru...

    苦咖啡
  • Restful风格的CRUD实现、Restful风格的Spring MVC实现

    对于整个现在求职来讲,包括工作来讲,掌握Restful架构的思想还是很重要的,一切都是资源操作。

    爱明依
  • SpringBoot与PageHelper的整合示例详解

    Dream城堡
  • 基于云开发的小程序海报功能的实现

    其实在最早之前的小程序中已经实现了一次,具体可以参考利用云开发优化博客小程序(三)——生成海报功能,主要还是使用原生的cavans进行组装,原本想代码copy过...

    Bug生活2048
  • 安恒杯丨你一定不知道的安恒杯新姿势

    经过了一周年的洗礼,安恒杯也逐渐明确了自己的定位:论规模,我们尚且不如国内几大知名赛事,毕竟是小(kui)本生意;论质量,我们也还有许多需要改善的地方。但我们还...

    安恒网络空间安全讲武堂
  • "Sed" 高级功能:我这小脑瓜都快绕晕了

    sed命令有两个空间,一个叫pattern space,一个叫hold space。这两个空间能够证明人类的脑瓜容量是非常小的,需要经过大量的训练和烧脑的理解,...

    xjjdog

扫码关注云+社区

领取腾讯云代金券