Python基础 - 装饰器总结

装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。

基本知识请参考:https://www.runoob.com/w3cnote/python-func-decorators.html

有些重要的东西提一下:

1. 使用 @ 标识符将装饰器应用到函数

Python2.4以上支持使用标识符@将装饰器应用在函数上,只需要在函数的定义前加上@和装饰器的名称。:

@wrapper
def add(a, b):
    return Coordinate(a.x + b.x, a.y + b.y)

多个decorator

@decorator_one
@decorator_two
def func():
    pass

相当于:

func = decorator_one(decorator_two(func))

比如:带参数的decorator:

@decorator(arg1, arg2)
def func():
    pass

相当于:

func = decorator(arg1,arg2)(func)

这意味着decorator(arg1, arg2)这个函数需要返回一个“真正的decorator”。

2.多个装饰器使用的调用顺序

def one(func):
    print('----1----')
    def two():
        print('----2----')
        func()
    return two

def a(func):
    print('----a----')
    def b():
        print('----b----')
        func()
    return b

@one
@a
def demo():
    print('----3----')

demo()

执行结果:

/usr/bin/python2.7 /home/python/Desktop/tornadoProject/one.py
----a----
----1----
----2----
----b----
----3----

3.更通用的装饰器

*args and **kwarg的使用参考:https://blog.csdn.net/qq_19446965/article/details/104070046

有了这招新的技能,我们随随便便就可以写一个能够记录下传递给函数参数的装饰器了。例子:

def logger(func):
    def inner(*args, **kwargs): #1
        print "Arguments were: %s, %s" % (args, kwargs)
        return func(*args, **kwargs) #2
    return inner

请注意我们的函数inner,它能够接受任意数量和类型的参数并把它们传递给被包装的方法,这让我们能够用这个装饰器来装饰任何方法。

@logger
def foo1(x, y=1):
    return x * y
@logger
def foo2():
    return 2
foo1(5, 4)
#Arguments were: (5, 4), {}
#20
foo1(1)
#Arguments were: (1,), {}
#1
foo2()
#Arguments were: (), {}
#2

随便调用我们定义的哪个方法,相应的内容也会打印到输出窗口,和我们预期的一样。

如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:

def log(text):
    def decorator(func):
        def wrapper(*args, **kw):
            print '%s %s():' % (text, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

这个3层嵌套的decorator用法如下:

@log('execute')
def now():
    print '2013-12-25'

执行结果如下:

>>> now()
execute now():
2013-12-25

和两层嵌套的decorator相比,3层嵌套的效果是这样的:

now = log('execute')(now)

我们来剖析上面的语句,首先执行log('execute'),返回的是decorator函数,再调用返回的函数,参数是now函数,返回值最终是wrapper函数。

4.装饰器的副作用

函数也是对象,它有__name__等属性,但你去看经过decorator装饰之后的函数,它们的__name__已经从原来的'now'变成了'wrapper'

>>> now.__name__
'wrapper'

因为返回的那个wrapper()函数名字就是'wrapper',所以,需要把原始函数的__name__等属性复制到wrapper()函数中,否则,有些依赖函数签名的代码执行就会出错。

不需要编写wrapper.__name__ = func.__name__这样的代码,Python内置的functools.wraps就是干这个事的,所以,一个完整的decorator的写法如下:

import functools

def log(func):
    @functools.wraps(func)
    def wrapper(*args, **kw):
        print 'call %s():' % func.__name__
        return func(*args, **kw)
    return wrapper

或者针对带参数的decorator:

import functools

def log(text):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kw):
            print '%s %s():' % (text, func.__name__)
            return func(*args, **kw)
        return wrapper
    return decorator

在定义wrapper()的前面加上@functools.wraps(func)即可。

5. class式的 Decorator

首先,先得说一下,decorator的class方式,还是看个示例:

class myDecorator(object):
 
    def __init__(self, fn):
        print "inside myDecorator.__init__()"
        self.fn = fn
 
    def __call__(self):
        self.fn()
        print "inside myDecorator.__call__()"
 
@myDecorator
def aFunction():
    print "inside aFunction()"
 
print "Finished decorating aFunction()"
 
aFunction()
 
# 输出:
# inside myDecorator.__init__()
# Finished decorating aFunction()
# inside aFunction()
# inside myDecorator.__call__()

1)一个是__init__(),这个方法是在我们给某个函数decorator时被调用,所以,需要有一个fn的参数,也就是被decorator的函数。
2)一个是__call__(),这个方法是在我们调用被decorator函数时被调用的。
上面输出可以看到整个程序的执行顺序。

这看上去要比“函数式”的方式更易读一些。

注意:如果decorator有参数的话,__init__() 成员就不能传入fn了,而fn是在__call__的时候传入的。

 

————————————————————————————

参考:

  1. https://www.runoob.com/w3cnote/python-func-decorators.html
  2. https://my.oschina.net/leejun2005/blog/477614
  3. https://segmentfault.com/a/1190000008373249
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值