装饰器
回顾闭包
- 存在函数嵌套的前提下
- 内部函数使用了外部函数的变量(入参或函数的局部变量)
- 外部函数返回这个内部函数
def outer_fun(a): # 外部函数
def inner_fun(b): # 内部函数
return b + a # 返回 b+a,a 属于外部函数
return inner_fun # 外部函数返回一个内部函数
fun = outer_fun(3) # 是一个函数
result = fun(2) # 调用这个函数(外部函数的入参 3 这里还能使用,要明白当前外部函数已经调用结束了,但是这里任能使用)
print(result) # 输出 5
不修改原有函数代码的基础上扩展原有函数的功能,本质上是一个闭包
装饰器语法
def decorator(fn): # 入参是一个方法
def inner():
"""目标方法执行前"""
fn() # 执行目标方法
"""目标方法执行后"""
return inner
@decorator # 使用 decorator 方法增强当前方法
def target():
pass
# 调用目标方法
target()
示例
def ext_say_hello(fun):
def inner():
print("张三说:", end="")
fun()
return inner
@ext_say_hello
def say_hello():
print("Hello World")
say_hello() # 张三说:Hello World
装饰器原理
原理如下,这个看起来稍微有点抽象,瞄一眼得了(也只是看起来抽象,其实不难理解)
# 满足闭包三要素:函数嵌套、内部函数使用外部函数变量(函数也可以作为变量)、外部函数返回内部函数
def ext_say_hello(fun):
def inner():
print("张三说:", end="")
fun()
return inner
# 要增强的方法
def say_hello():
print("Hello World")
# 使用 ext_say_hello 增强 say_hello
# ext_say_hello 返回的是一个函数,使用 () 调用一下
# @ext_say_hello 等价于 ext_say_hello(say_hello)
ext_say_hello(say_hello)() # 输出 张三说:Hello World
带参数的装饰器
三层函数嵌套
def who_to_zhangsan_say_hello(name):
def zhangsan_say_hello(fun):
def inner():
print(f"{name}对张三说:", end="")
fun()
return inner
return zhangsan_say_hello
@who_to_zhangsan_say_hello("李四")
def say_hello():
print("Hello World")
say_hello() # 李四对张三说:Hello World
@who_to_zhangsan_say_hello("李四")
分析,由 @
和 who_to_zhangsan_say_hello("李四")
组成
- who_to_zhangsan_say_hello 方法是加了括号的,可以理解为调用了
- who_to_zhangsan_say_hello 方法的返回值是是 zhangsan_say_hello 方法
- 所以
who_to_zhangsan_say_hello("李四")
也就是zhangsan_say_hello
- 所以
@who_to_zhangsan_say_hello("李四")
也就是@zhangsan_say_hello
如果 目标函数有参数:
def who_to_zhangsan_say_hello(name):
def zhangsan_say_hello(fun):
def inner(*args, **kwargs):
print(f"{name}对张三说:", end="")
fun(*args, **kwargs)
return inner
return zhangsan_say_hello
@who_to_zhangsan_say_hello("李四")
def say_hello(words):
print(words)
say_hello("hello world") # 输出 李四对张三说:hello world
名称伪装
def who_to_zhangsan_say_hello():
def zhangsan_say_hello(fun):
def inner(*args, **kwargs):
fun(*args, **kwargs)
return inner
return zhangsan_say_hello
@who_to_zhangsan_say_hello()
def say_hello(words):
print(words)
print(say_hello.__name__) # 输出 inner。因为这个函数已经被 inner 替换了,所以输出的是 inner 的名字
虽然能想通,但是如果能让名字保持原来的更易于可读性,这就是伪装
from functools import wraps
def who_to_zhangsan_say_hello():
def zhangsan_say_hello(fun):
@wraps(fun) # 使用 wraps 伪装成目标函数名字
def inner(*args, **kwargs):
fun(*args, **kwargs)
return inner
return zhangsan_say_hello
@who_to_zhangsan_say_hello()
def say_hello(words):
print(words)
print(say_hello.__name__) # 输出 say_hello
属性装饰器
看一个传统的私有属性示例:
class MyClass:
def __init__(self):
self.__age = 0 # 双下划线,真私有属性,只能内部访问,所以提供一组 getter 和 setter
def age_getter(self):
return self.__age
def age_setter(self, new_age):
self.__age = new_age
if __name__ == '__main__':
mc = MyClass()
# print(mc.__age) # 报错。没有这个属性,之前解释过了这是解释器修改了名称
print(mc.age_getter()) # 输出 0
mc.age_setter(1) # 设置为 1
print(mc.age_getter()) # 输出 1
属性装饰器就是把方法当做属性的方式来使用,方便一点(感觉上面的写法也不麻烦呀,可能 java 写多吧)
class MyClass:
def __init__(self):
self.__age = 0
# getter。1,函数名称为 属性名称;2,加上 @property 修饰
@property
def age(self):
return self.__age
# setter。1,函数名称为 属性名称;2,加上 @属性名.setter 修饰
@age.setter # setter,
def age(self, new_age):
self.__age = new_age
if __name__ == '__main__':
mc = MyClass()
print(mc.age) # 输出 0。调用 age 的 getter
mc.age = 1 # 输出 1。调用 age 的 setter
print(mc.age) # 输出 1
# mc.age() # 报错。因为 mc.age 表示调用 getter 已经回返回一个结果1。相当于 1()