装饰器 decorator

装饰器 decorator

标签: python


这里学习一下Python装饰器(Decorator)的用法。
首先要注意的是,Python中的函数可以像普通变量一样当做参数传递给另外一个函数,例如:

def foo():
    print('foo')

def bar(func):
    print('bar start')
    func()
    print('bar end')

bar(foo)

输出为:

bar start
foo
bar end

实际上装饰器本质上是python的一个函数或类,它可以在其他函数或类在不需要修改代码的前提下,附加某些额外功能。装饰器的返回值也是一个函数或类的对象。
它经常用于有切面需要的场景,比如:插入日志、性能测试、事务处理、缓存、教研权限等,装饰器是一个绝佳的选择。
我们可以抽离出与函数本身无关,但又在多个函数中存在需要的相同代码作为装饰器,并继续重用。

现在,假设我们要给foo()增加功能,在执行函数时自动打印日志,又不修改原函数的内容。

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

@log
def foo():
    print('foo')

执行结果为:

>>> foo()
foo() is running...
foo1

像这样把@log方法函数定义的开头,相当于执行了语句:

foo = log(foo)

由于log()是一个装饰器,其返回一个函数。所以原来的foo()函数依然存在,只是同名的函数指向了一个新的函数,于是调用foo()将执行新的函数,即在log()函数中返回的wrapper()函数。
这里的wrapper()函数只是作为一个原函数的代替。

wrapper()函数的参数定义为(*args, **kw),因此,
wrapper()函数可以接收任意参数的调用。在wrapper()函数内,首先打印日志,然后执行原函数。

decorator需要传入参数

如果Decorator本身需要传入参数,那么就需要编写一个返回decorator的高阶函数。比如要自定义log的文本。

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

@log('is runningggggg ......')
def foo(text):
    print(text)
>>> foo('sdfsdfsdfsdf')
foo() is runningggggg ......
sdfsdfsdfsdf

三层嵌套的实际效果为:

foo = log(text)(foo)


返回函数的函数
log返回decorator,然后寻找decorator的定义,将foo作为参数传入,decorator返回wrapper,再寻找def wrapper(),将foo的参数传入,打印日志并返回foo(*args, **kw)的结果。


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

到这里,实际上foo已经被换做为wrapper了:

>>> foo.__name__
'wraper'

这样实际上我们还是修改了一部分内容,当需要用到函数名字作为依赖项时就会出现问题。
Python内置的functools.wraps就可以解决这个问题,也就是执行了wrapper.__name__ = func.__name__这样的代码。

一个完整的Decorator写法如下:

import functools

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

@log
def foo():
    print('fooooo')

foo()
print(foo.__name__)

执行结果:

call foo():
fooooo
foo

带参数的log:

import functools


def log1(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

@log1('ssssssssssss')
def foo1():
    print('foo111111')

foo1()
foo1.__name__

结果:

ssssssssssss foo1()
foo111111
'foo1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值