Python 元编程 - 装饰器

Python 中提供了一个叫装饰器的特性,用于在不改变原始对象的情况下,增加新功能或行为。

这也属于 Python "元编程" 的一部分,在编译时一个对象去试图修改另一个对象的信息,实现 "控制一切" 目的。

本篇文章作为装饰器的基础篇,在阅读后应该了解如下内容:

装饰器的原理?

装饰器如何包裹有参数的函数?

装饰器本身需要参数怎么办?

被装饰器修饰的函数还是原函数吗,怎么解决?

装饰器嵌套时的顺序?

装饰器常见的应用场景?

装饰器原理

在具体装饰器的内容前,先来回顾下 Python 中的基本概念:

Python 中,一切都是对象,函数自然也不例外

python 中的对象都会在内存中用于属于自己的一块区域。在操作具体的对象时,需要通过 “变量” ,变量本身仅是一个指针,指向对象的内存地址。

函数作为对象的一种,自然也可以被变量引用。

def hello(name: str): print('hello', name) hello('Ethan') alias_func_name = hello alias_func_name('Michael') # hello Ethan # hello Michael

alias_func_name 作为函数的引用,当然也可以作为函数被使用。

函数接受的参数和返回值都可以是函数

def inc(x): return x + 1 def dec(x): return x - 1 def operate(func, x): result = func(x) return result operate(inc,3) # 4 operate(dec,3) # 2

这里 operate 中接受函数作为参数,并在其内部进行调用。

嵌套函数

def increment(): def inner_increment(number): return 1 + number return inner_increment() print(increment(100)) # 101

在 increment 内部,实现对 number add 1 的操作。

回头再来看下装饰器的实现:

# def decorator def decorator_func(func): print('enter decorator..') def wrapper(): print('Step1: enter wrapper func.') return func() return wrapper # def target func def normal_func(): print("Step2: I'm a normal function.") # use decorator normal_func = decorator_func(normal_func) normal_func()

decorator_func(func) 中,参数 func 表示想要调用的函数,wrapper 为嵌套函数,作为装饰器的返回值。

wrapper 内部会调用目标函数 func 并附加自己的行为,最后将 func 执行结果作为返回值。

究其根本,是在目标函数外部套上了一层 wrapper 函数,达到在不改变原始函数本身的情况下,增加一些功能或者行为。

通常使用时,使用 @decorator_func 来简化调用过程的两行代码。

将自定义调用装饰器的两行代码删掉,使用常规装饰器的写法加在 normal_func 的定义处,但却不调用 normal_func,可以发现一个有趣的现象:

# def decorator def decorator_func(func): print('enter decorator..') def wrapper(): print('Step1: enter wrapper func.') return func() return wrapper # def target func @decorator_func def normal_func(): print("Step2: I'm a normal function.")

发现 enter decorator.. 在没有调用的情况下被打印到控制台。

这就说明,此时 normal_func 已经变成了 wrapper 函数。

@decorator_func 其实隐含了 normal_func = decorator_func(normal_func) 这一行代码。

对带有参数的函数使用装饰器

假设这里 normal_func 需要接受参数怎么办?

很简单,由于是通过嵌套函数来调用目标函数,直接在 wrapper 中增加参数就可以了。

# def decorator def decorator_func(func): def wrapper(*args, **kwargs): print('Step1: enter wrapper func.') return func(*args, **kwargs) return wrapper # def target func def normal_func(*args, **kwargs): print("Step2: I'm a normal function.") print(args) print(kwargs) # use decorator normal_func = decorator_func(normal_func) normal_func(1, 2, 3,, sex='boy')

使用 *args, **kwargs 是考虑到该 decorator 可以被多个不同的函数使用,而每个函数的参数可能不同。

装饰器本身需要参数

在装饰器本身也需要参数时,可以将其嵌套在另一个函数中,实现参数的传递。

# def decorator def decorator_with_args(*args, **kwargs): print('Step1: enter wrapper with args func.') print(args) print(kwargs) def decorator_func(func): def wrapper(*args, **kwargs): print('Step2: enter wrapper func.') return func(*args, **kwargs) return wrapper return decorator_func # def target func def normal_func(*args, **kwargs): print("Step3: I'm a normal function.") print(args) print(kwargs) normal_func = decorator_with_args('first args')(normal_func) normal_func('hello') # use @ to replace the above three lines of code @decorator_with_args('first args') def normal_func(*args, **kwargs): print("Step3: I'm a normal function.") print(args) print(kwargs)

来分析下 decorator_with_args 函数:

由于 decorator_with_args 接受了任意数量的参数,同时由于 decorator_func 和 wrapper 作为其内部嵌套函数,自然可以访问其内部的作用域的变量。这样就实现了装饰器参数的自定义。

decorator_func 是正常的装饰器,对目标函数的行为进行包装。进而需要传递目标函数作为参数。

在使用时:

@decorator_with_args('first args') 实际上做的内容,就是 normal_func = decorator_with_args('first args')(normal_func) 的内容:

decorator_with_args('first args') 返回 decorator_func 装饰器。

decorator_func 接受的正常函数对象作为参数,返回包装的 wrapper 对象。

最后将 wrapper 函数重命名至原来的函数,使其在调用时保持一致。

保留原函数信息

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywyfx.html