刚刚完成 由 Python 的 Ellipsis 到 *, /, *args, **kwargs 函数参数, 又回想起在 熟悉和应用 Python 的装饰器,关于带属性的装饰器一直未交代,安心不下来,Python 中带属性的装饰器用得非常普遍,如 Flask 的 @app.route('/')
。
我们一看到 Python 的装饰器(Decorator) 会很直截的与 Java 的注解(Annotation) 联系起来,其实除了都用 @
符号外是存在很大区别的。正如它们被翻译成的中文名那样,Java 的注解在一定程度上就是一个注释,只要没有注解处理器处理它们就可以被忽略,Java 要用反射来处理注解。而 Python 的装饰器更象是代理,函数一旦被装饰后,调用目标函数时是无法挣脱装饰器函数的控制的,是硬核的。
在 Python 中被装饰的函数想要在执行期跳过装饰器函数的附加行为,能想到的一个比喻就是像踢开**闹**, 除非上面内部的通容才能做到。
回到 Python 的装饰器的话题上来,我们除了让装饰器可以传递函数参数,接受函数的返回值外,现在要给装饰器加上属性。在原来的装饰器实现上进行改进,在 熟悉和应用 Python 的装饰器 一文中有个例子是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
from functools import wraps def my_decorator(func): @wraps(func) def wrapper(*args, **kwargs): print(f"before calling {func.__name__}") func(*args, **kwargs) print(f"after calling {func.__name__}") return wrapper @my_decorator def say_hello(firstname, lastname, **kwargs): print(f"Hello {firstname} {lastname}!", kwargs) say_hello("Steve", "Jobs", company="Apple", country="USA") |
如果我们想要的装饰器能写成
@my_decorator(path='/hello_world')
那我们的 my_decorator 实现上还得加上一个层次
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from functools import wraps def my_decorator(path): def inner_decorator(func): @wraps(func) def wrapper(*args, **kwargs): print(f"input path {path}") print(f"before calling {func.__name__}") func(*args, **kwargs) print(f"after calling {func.__name__}") return wrapper return inner_decorator @my_decorator(path="/hello_world") def say_hello(firstname, lastname, **kwargs): print(f"Hello {firstname} {lastname}!", kwargs) say_hello("Steve", "Jobs", company="Apple", country="USA") |
装饰器实现最外层的函数不再是直接面对函数(以函数为参数),而是将要的属性作为参数。执行上面的代码输出为
input path /hello_world
before calling say_hello
Hello Steve Jobs! {'company': 'Apple', 'country': 'USA'}
after calling say_hello
同样的,装饰器的机制是,得到原函数包装后的函数,再调用该 wrapper 函数,所以按照这机制不用 @my_decorator 的话,就是
1 |
my_decorator(path="/hello_world")(say_hello)("Steve", "Jobs", company="Apple", country="USA") |
还有一个更巧妙的实现,可以利用 my_decorator 的一个参数,时为 function 时为 path, 见 Python 的 functools.py 中 https://github.com/python/cpython/blob/main/Lib/functools.py#L508
1 2 3 4 5 6 7 8 9 10 11 |
def lru_cache(maxsize=128, typed=False): ... if isinstance(maxsize, int): # Negative maxsize is treated as 0 if maxsize < 0: maxsize = 0 elif callable(maxsize) and isinstance(typed, bool): # The user_function was passed in directly via the maxsize argument user_function, maxsize = maxsize, 128 ... |
接下来还有更多的关于 Python 装饰器的内容,如用类代替函数作为装饰器等
本文链接 https://yanbin.blog/python-decorator-with-property/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。