我在之前一直在分享一些Python小工具,例如
- Python小工具系列(4)之自动重试
- Python小工具系列(3)之参数检查
- Python小工具系列(1)之file_cache
都是以装饰器的形式的小工具。今天就来介绍一下,Python装饰器(decorator)是啥。
Python装饰器(decorator)是Python语言中的语法糖。其本质也是一种函数,它可以非侵入式的扩展原有的原函数功能。可以让你优雅编程,把编程变成一门艺术。
理解Python的装饰器,首先要知道在Python里一切皆为对象。所以函数也是对象,可以被赋到一个变量上,甚至class也是如此。那么既然函数可以被赋给变量。那么是不是可以把函数也当做参数传入,并且对其进行修改,比如说添加用法或者添加参数等。这就是装饰器的本质。
简单来说就是他是一个语法糖,通过外部复写传入的函数,达到修改函数的目的。所以装饰器的特点就是自身返回值也是一个函数,一个全新的函数。
场景设想
在这里描述一个场景,这样也可以好好装饰器的魅力感受一下:
假如在工作中,有一个简单的需求,比如写统计一个数据集(list)中某个数的个数。
def count(a:list, type:int): return len(list(filter(lambda x: x ==type ,a)))
你看很简单,一行代码搞定。
如果该函数被很多函数调用,并且被集成至很多地方。那么就有如下两个特点:
- 代码复用性很高,pretty good!
- 代码冗余较小,干净。So pythonic!
但是如果你想修改一下count函数,那么就很危险了。因为目前count函数被很多其他函数调用,意味着对它修改者牵一发而动全的处境。
这时候,装饰器就可以闪亮登场了。使用python装饰器的好处就是在不用更改原函数的代码前提下给函数增加新的功能。
比如说我想为count函数添加打印最终结果到终端的效果。那么就直接定义如下装饰器即可
def print2stdout(func):def wraps(*args,**kwargs): res = func(*args,**kwargs)print(res)return resreturn wraps
然后给count打上装饰器
@print2stdoutdef count(a:list, type:int): return len(list(filter(lambda x: x ==type ,a)))
这样可以最小程度修改函数的情况下,为count函数添加打印功能。
装饰器的副作用
到这里,如果你已经了解了装饰器,你可能会意识到一个问题:
- 既然装饰器的返回值是一个函数,那么这个函数的元属性是否跟原始函数一样
答案是不一样的。
那么就需要标准库中的functools包中wraps来复原函数原始信息。在定义装饰器的时候来保留原始的原信息
具体做法如下:
def print2stdout(func):@wraps(func)def wraps(*args,**kwargs): res = func(*args,**kwargs)print(res)return resreturn wraps
使用场景
现在我们来看一下装饰器在哪些地方可以使用。
授权(Authorization)
装饰器可以帮助进行鉴权。这种用法大部分用在web框架中,比如说Flask和Django等。
这里是一个例子来使用基于装饰器的授权:
from functools import wraps def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): auth = request.authorization if not auth or not check_auth(auth.username, auth.password): authenticate() return f(*args, **kwargs) return decorated
代码参考于https://www.runoob.com/w3cnote/python-func-decorators.html
参数检查
这个用例可以参考我之前的一篇小工具Python小工具系列(3)之参数检查
自动重试
这个用例可以参考我之前的一篇小工具Python小工具系列(4)之自动重试
还有其他用法在此就不在一一列举了。