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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341