python decorators

1.什么是装饰器

A decorator is the name used for a software design pattern. Decorators dynamically alter the functionality of a function, method, or class without having to directly use subclasses or change the source code of the function being decorated.
Essentially, decorators work as wrappers, modifying the behavior of the code before and after a target function execution, without the need to modify the function itself, augmenting the original functionality, thus decorating it.

This is why decorators are also called 'wrappers'. What Python does when it sees @decorator is to call the decorator function with the wrapped function as it's first argument (in this case the show_page function).

这两段话说得太好,以至于我不知道该如何翻译才能保留他的原汁原味。

2.预备知识:函数

在python中,函数可以说是一等公民,它们也是对象,我们可以用它来做很多有用的东西。比如:把函数分配给变量,在其他函数中定义函数,函数可以当做参数传给其他函数,函数可以返回其他函数。

装饰器的两大特性:

  • 1.能把被装饰的函数替换成其他函数
  • 2.装饰器在加载模块时立即执行
    装饰器的典型行为: 把被装饰的函数替换成新的函数,二者接收相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做些额外的操作。

3.装饰器语法:

函数装饰器

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

@p_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")

# Outputs <p>lorem ipsum, John dolor sit amet</p>
def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

def strong_decorate(func):
    def func_wrapper(name):
        return "<strong>{0}</strong>".format(func(name))
    return func_wrapper

def div_decorate(func):
    def func_wrapper(name):
        return "<div>{0}</div>".format(func(name))
    return func_wrapper

@div_decorate
@p_decorate
@strong_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")

# Outputs <div><p><strong>lorem ipsum, John dolor sit amet</strong></p></div>

装饰方法

def p_decorate(func):
   def func_wrapper(*args, **kwargs):
       print args, kwargs  # (<__main__.Person object at 0x00000000025CB160>,) {}
       return "<p>{0}</p>".format(func(*args, **kwargs))
   return func_wrapper

class Person(object):
    def __init__(self):
        self.name = "John"
        self.family = "Doe"

    @p_decorate
    def get_fullname(self):
        return self.name+" "+self.family

my_person = Person()

print my_person.get_fullname()
# <p>John Doe</p>

传参数到装饰器中

def tags(tag_name):
    def tags_decorator(func):
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name))
        return func_wrapper
    return tags_decorator

@tags("p")
def get_text(name):
    return "Hello "+name

print get_text("John")
# <p>Hello John</p>

4.技巧

这里有几点需要特别注意的地方当写一个wrapper函数时:

  • Always accept and pass on all arguments - use (*args, **kwargs)
    when defining the wrapper and when calling the function. Exceptions are when you're intercepting arguments or modifying them. Look here for more information about this.
  • Don't forget to return the value that the wrapped function returns. I've lost track of the number of times I've found my decorated functions returning None, all because I forgot to type in return. The exception here is again if you want to intercept return values and check them for errors or do other things with them.

5.特别注意的一点

首先看看下面的例子:

import functools

def decoratorer(func):
#     @functools.wraps(func)
    def wapper(*args,**kwargs):
        print args, kwargs
        result = func(*args,**kwargs)
        print func, result
        return result
    return wapper

registry = []
def register(func):
    registry.append(func)
    return func

@register
@decoratorer
def add(a,b):
    return a+b

if __name__ == '__main__':
    print add(1, 3)
    print registry

运行结果:

未加保护的.png

去除注释后的运行结果:
加保护的.png

装饰器函数遮盖了被装饰函数的__name____doc__ 属性。使用 functools.wraps 装饰器把相关的属性从 func 复制到 wapper中

6.多个装饰器的执行顺序

还是这个例子

import functools

def decoratorer(func):
    @functools.wraps(func)
    def wapper(*args,**kwargs):
        print args, kwargs
        result = func(*args,**kwargs)
        print func, result
        return result
    return wapper

registry = []
def register(func):
    registry.append(func)
    print registry
    return func

@register
@decoratorer
def add(a,b):
    return a+b

if __name__ == '__main__':
    print add(1, 3)

运行结果:

[<function add at 0x0000000002FD0F28>]
(1, 3) {}
<function add at 0x0000000002FD0EB8> 4
4

装饰器的应用是自底向上的,读取代码时,首先读到的是上面装饰器的内容。这就是出现上面运行结果的原因。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。