python--函数装饰器的解析

装饰器:用于在源码中“标记”函数,在代码运行期间动态增强函数的行为的方式。
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
函数是可调用的对象,其参数是另一个函数(被装饰的函数)。装饰器可能会处理被装饰的函数,然后把它返回,或者将其替换成另一个函数或可调用对象。

我们先看看装饰器的第一个特性

1、动态增强被装饰函数的功能

import time

def now():
    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

now()
输出:
2020-07-07 14:04:37

现在我们要增强这个函数,比如说在打印时间时自动打印一句话,“此刻的日期是:”,但是我们又不想修改原来的now()的函数代码,此时就可以利用装饰器。


```python
import time

def dec_fun(func):#装饰函数要定义在被装饰函数之前
    def wrapper(*args, **kwargs):
        print("此刻的日期是:")
        return func(*args, **kwargs)
    return wrapper
@dec_fun#表示用dec_fun函数装饰now()
def now():
    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

now()
输出:
此刻的日期是:
2020-07-07 14:12:33

2、装饰器还可以将被装饰函数替换成其他函数。

此时只要我们在装饰器函数中不返回传入装饰器的函数,而是只返回装饰器定义的函数就行了。

import time

def dec_fun(func):#装饰函数要定义在被装饰函数之前
    def wrapper(*args, **kwargs):
        print("不要打印日期了")
    return wrapper
@dec_fun#表示用dec_fun函数装饰now()
def now():
    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

now()
输出:
不要打印日期了

3、当装饰器也需要传入自己的参数

前面说的装饰器都是没有自己的参数的,它传入的是被装饰的函数,当装饰器也需要传入自己的函数时,怎么办呢?只要在之前的装饰器外再套一个带参数的函数,这个函数返回之前的装饰器。

import time

def log(name):
    def dec_fun(func):
        def wrapper(*args,**kwargs):
            print(name+"正在打印日期:")
            return func(*args, **kwargs)
        return wrapper
    return dec_fun
@log("python")
def now():
    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))

now()
输出:
python正在打印日期:
2020-07-07 14:24:01

4、装饰器在加载模块是立即执行

装饰器的一个关键特性是,它们在被装饰的函数定义之后立即运行。这通常是在导入时(即 Python 加载模块时)

registry = []
def register(func):
    print('running register(%s)' % func)
    registry.append(func)
    return func
@register
def f1():
    print('running f1()')
@register
def f2():
    print('running f2()')
def f3():
    print('running f3()')
def main():
    print('running main()')
    print('registry ->', registry)
f1()
f2()
f3()
if __name__=='__main__':
    main()
输出:
running register(<function f1 at 0x000001B8F02072F0>)
running register(<function f2 at 0x000001B8F0207378>)
running f1()
running f2()
running f3()
running main()
registry -> [<function f1 at 0x000001B8F02072F0>, <function f2 at 0x000001B8F0207378>]
registry = []
def register(func):
    print('running register(%s)' % func)
    registry.append(func)
    return func
@register
def f1():
    print('running f1()')
@register
def f2():
    print('running f2()')
def f3():
    print('running f3()')
def main():
    print('running main()')
    print('registry ->', registry)
# f1()
# f2()
# f3()
if __name__=='__main__':
    main()
输出:
running register(<function f1 at 0x00000257DFBC72F0>)
running register(<function f2 at 0x00000257DFBC7378>)
running main()
registry -> [<function f1 at 0x00000257DFBC72F0>, <function f2 at 0x00000257DFBC7

从上面可以看出,被装饰函数定义之后,装饰器立即执行。

5、不带参数的类装饰器

以上都是基于函数实现的装饰器,基于类装饰器的实现,类必须实现__init__和__call__两个内置函数。
init:接收被装饰函数
call:实现装饰逻辑

class logger(object):
    def __init__(self, func):
        self.func = func

    def __call__(self, *args, **kwargs):
        print("[INFO]: the function {func}() id running..."
              .format(func=self.func.__name__))
        return self.func(*args, **kwargs)

@logger
def say(something):
    print("say {}!".format(something))

if __name__ == "__main__":
    say("hello")
#############################################
[INFO]: the function say() id running...
say hello!

6、带参数的类装饰器

上面不带参数的例子,你发现没有,只能打印INFO级别的日志,正常情况下,我们还需要打印DEBUG WARNING等级别的日志。这就需要给类装饰器传入参数,给这个函数指定级别了。带参数和不带参数的类装饰器有很大的不同。
__ init __ :不再接收被装饰函数,而是接收传入参数。
__ call __ :接收被装饰函数,实现装饰逻辑。

class logger(object):
    def __init__(self, level="INFO"):
        self.level = level


    def __call__(self, func):
        def wrapper(*args,**kwargs):
            print("[{level}]: the function {func}() id running..."
                .format(level=self.level,func=func.__name__))
            func(*args, **kwargs)
        return wrapper

@logger(level="WARNING")
def say(something):
    print("say {}!".format(something))

if __name__ == "__main__":
    say("hello")

################################################
[WARNING]: the function say() id running...
say hello!

7、闭包

闭包是指延伸了作用域的函数,其中包含函数定义体中引用、但是不在定义体中定义的非全局变量。
闭包一种函数,它会保留定义函数时存在的自由变量(未在本地作用域绑定的变量)的绑定,这样调用函数时,虽然定义域不可用了,但是仍能使用那些绑定。

nonlocal声明可以将变量标记为自由变量,即在函数中为变量赋予新值了,也会变成自由变量

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值