前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python 装饰器(Decorator)

Python 装饰器(Decorator)

作者头像
Steve Wang
发布2018-02-05 17:43:31
7030
发布2018-02-05 17:43:31
举报
文章被收录于专栏:从流域到海域从流域到海域
引入

如果你学过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这里就只是简单的一句注释。

代码语言:javascript
复制
#下面是来自维基百科的一个例子
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函数所具有的功能,相当于装饰器函数(被装饰函数)即装饰器函数调用了被装饰的函数,被装饰函数作为参数传进去,所以装饰器函数可以使用被装饰函数的功能,同时又可以使用自身的功能,从而实现装饰效果。

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

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

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

代码语言:javascript
复制
#完全体装饰器 对维基上的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

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

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017年12月19日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 引入
  • Python 装饰器(Decorator)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档