Avant de parler des décorateurs Python, je voudrais donner un exemple bien que ce soit un peu sale, il est très pertinent pour le sujet des décorateurs.
La fonction principale des sous-vêtements que chacun possède est de couvrir notre honte, mais en hiver il ne peut pas nous protéger du vent et du froid, que devons-nous faire ? Une façon à laquelle nous avons pensé était de transformer les sous-vêtements pour les rendre plus épais et plus longs. De cette façon, ils ont non seulement pour fonction de couvrir la honte, mais aussi de fournir de la chaleur. Cependant, il y a un problème après avoir transformé les sous-vêtements en pantalons. , , bien qu'il ait toujours une fonction de couverture de honte, il ne s'agit essentiellement plus d'un véritable sous-vêtement. Les gens intelligents ont donc inventé le pantalon et l'ont mis directement sur le sous-vêtement sans affecter le sous-vêtement. Avec le pantalon, le bébé n'aura plus froid. Les décorateurs sont comme les pantalons dont nous parlons ici. Ils apportent de la chaleur à notre corps sans affecter la fonction des sous-vêtements.
Avant de parler de décorateurs, vous devez d'abord comprendre une chose. Les fonctions en Python sont différentes de Java et C. Les fonctions en Python peuvent être transmises à une autre fonction en tant que paramètres comme des variables ordinaires, par exemple :
def foo(): print("foo") def bar(func): func() bar(foo)
Retour officiel à notre sujet. Un décorateur est essentiellement une fonction ou une classe Python qui permet à d'autres fonctions ou classes d'ajouter des fonctionnalités supplémentaires sans apporter de modifications au code. La valeur de retour du décorateur est également un objet fonction/classe. Il est souvent utilisé dans des scénarios comportant des exigences transversales, telles que : l'insertion de journaux, les tests de performances, le traitement des transactions, la mise en cache, la vérification des autorisations, etc. Les décorateurs sont une excellente conception pour résoudre de tels problèmes. Avec les décorateurs, nous pouvons extraire une grande quantité de code similaire qui n'a rien à voir avec la fonction elle-même dans les décorateurs et continuer à le réutiliser. En un mot, le but d’un décorateur est d’ajouter des fonctionnalités supplémentaires à un objet existant.
Regardons d'abord un exemple simple, bien que le code réel puisse être beaucoup plus compliqué que cela :
def foo(): print('i am foo')
Maintenant, il y a une nouvelle exigence, dans l'espoir d'enregistrer le journal d'exécution du fonction, donc dans le code Ajoutez le code de journal dans :
def foo(): print('i am foo') logging.info("foo is running")
Et si les fonctions bar() et bar2() ont des exigences similaires ? Écrire une autre journalisation dans la fonction bar ? Cela entraîne beaucoup de code similaire. Afin de réduire l'écriture répétée de code, nous pouvons le faire et redéfinir une nouvelle fonction : spécifiquement pour traiter le journal, une fois le journal traité, le vrai code métier est exécuté
<🎜. >def use_logging(func): logging.warn("%s is running" % func.__name__) func() def foo(): print('i am foo') use_logging(foo)
def use_logging(func): def wrapper(): logging.warn("%s is running" % func.__name__) return func() # 把 foo 当做参数传递进来时,执行func()就相当于执行foo() return wrapper def foo(): print('i am foo') foo = use_logging(foo) # 因为装饰器 use_logging(foo) 返回的时函数对象 wrapper,这条语句相当于 foo = wrapper foo() # 执行foo()就相当于执行 wrapper()
def use_logging(func): def wrapper(): logging.warn("%s is running" % func.__name__) return func() return wrapper @use_logging def foo(): print("i am foo") foo()
def foo(name): print("i am %s" % name)
def wrapper(name): logging.warn("%s is running" % func.__name__) return func(name) return wrapper
def wrapper(*args): logging.warn("%s is running" % func.__name__) return func(*args) return wrapper
def foo(name, age=None, height=None): print("I am %s, age %s, height %s" % (name, age, height))
def wrapper(*args, **kwargs): # args是一个数组,kwargs一个字典 logging.warn("%s is running" % func.__name__) return func(*args, **kwargs) return wrapper
装饰器还有更大的灵活性,例如带参数的装饰器,在上面的装饰器调用中,该装饰器接收唯一的参数就是执行业务的函数 foo 。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。比如,我们可以在装饰器中指定日志的等级,因为不同业务函数可能需要的日志级别是不一样的。
def use_logging(level): def decorator(func): def wrapper(*args, **kwargs): if level == "warn": logging.warn("%s is running" % func.__name__) elif level == "info": logging.info("%s is running" % func.__name__) return func(*args) return wrapper return decorator @use_logging(level="warn") def foo(name='foo'): print("i am %s" % name) foo()
上面的 use_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level=”warn”)调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。
@use_logging(level=”warn”)等价于@decorator
没错,装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。
class Foo(object): def __init__(self, func): self._func = func def __call__(self): print ('class decorator runing') self._func() print ('class decorator ending') @Foo def bar(): print ('bar') bar()
使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子:
# 装饰器 def logged(func): def with_logging(*args, **kwargs): print func.__name__ # 输出 'with_logging' print func.__doc__ # 输出 None return func(*args, **kwargs) return with_logging # 函数 @logged def f(x): """does some math""" return x + x * x logged(f)
不难发现,函数 f 被with_logging取代了,当然它的docstring,__name__就是变成了with_logging函数的信息了。好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器里面的 func 函数中,这使得装饰器里面的 func 函数也有和原函数 foo 一样的元信息了。
from functools import wraps def logged(func): @wraps(func) def with_logging(*args, **kwargs): print func.__name__ # 输出 'f' print func.__doc__ # 输出 'does some math' return func(*args, **kwargs) return with_logging @logged def f(x): """does some math""" return x + x * x
一个函数还可以同时定义多个装饰器,比如:
@a @b @c def f (): pass
它的执行顺序是从里到外,最先调用最里层的装饰器,最后调用最外层的装饰器,它等效于
f = a(b(c(f)
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!