Décorateurs Python – décorateurs de fermetures et de fonctions

PHPz
Libérer: 2023-04-10 14:51:07
avant
1653 Les gens l'ont consulté

1. Fermetures

Avant d'apprendre les décorateurs, vous devez comprendre le concept des fermetures. Les points clés de la formation d'une fermeture :

  • Imbrication de fonctions
  • Utiliser la fonction interne comme valeur de retour de la fonction externe
  • La fonction interne doit utiliser les variables de la fonction externe

Ce qui suit est un cas de calcul la moyenne d'une liste pour expliquer la fermeture Package :

def make_average():
# 创建一个列表,用来保存数值
nums = []

# 定义一个内部函数,用来计算列表的平均值
def average(n):
# 将数值添加到列表中
nums.append(n)
# 返回平均值
return sum(nums) / len(nums)

return average
Copier après la connexion
  1. Premièrement, définissez une fonction make_average ;
  2. Deuxièmement, définissez une liste vide dans la fonction make_average pour stocker les valeurs
  3. Troisièmement, définissez une fonction interne pour calculer la moyenne de ; la liste;
  4. Enfin, utilisez cette fonction interne comme valeur de retour de la fonction externe make_average. Attention, l'ajout de () deviendra un appel à cette fonction.
# 调用外部函数,并将其复制给一个变量,注意:此时返回的是内函数的内存地址
a = make_average()
# 给这个变量加(),就相当于调用了内函数average
print(a(20))
print(a(30))
Copier après la connexion

Les résultats d'exécution sont les suivants : lorsque la valeur transmise est 20, il n'y a qu'un seul nombre dans la liste, donc le résultat du calcul est 20 ; lorsqu'une autre valeur 30 est transmise, il y a deux nombres 20 et 30 ; dans la liste. , donc le résultat calculé de la moyenne est 25.

Décorateurs Python – décorateurs de fermetures et de fonctions

2. Décorateur

1. Introduction au décorateur

Par exemple, il existe les deux fonctions suivantes, qui calculent la somme et le score de deux nombres. respectivement :

def add(a, b):
"""计算两数之和"""
res = a + b
return res

def mul(a, b):
"""计算两数之积"""
res = a * b
return res
Copier après la connexion

Maintenant, il y a une exigence : je veux imprimer "Démarrer le calcul..." avant le début du calcul de chaque fonction, et imprimer "Fin du calcul..." après la fin du calcul. Nous pouvons répondre à cette demande en modifiant directement le code de la fonction, mais cela se heurtera aux problèmes suivants :

  1. S'il y a trop de fonctions à modifier, dix voire cent fonctions, ce n'est pas réaliste ;
  2. Ce n'est pas pratique ; pour une maintenance ultérieure, par exemple, je ne veux pas imprimer « Démarrer le calcul... », mais imprimer « commencer... » Ne serait-il pas nécessaire de le modifier à nouveau
  3. Viole le principe d'ouverture et de fermeture ? (OCP), c'est-à-dire que la conception du programme nécessite une ouverture. Extension du programme et fermeture de la modification du programme

Ainsi, la méthode ci-dessus pour modifier directement le code de fonction n'est pas réalisable ; Nous espérons étendre la fonction sans modifier la fonction d'origine. Par exemple :

def new_add(a, b):
print("开始计算...")
r = add(a, b)
print("计算结束...")
return r


print(new_add(22, 33))
Copier après la connexion

Le résultat de l'exécution est le suivant :

Décorateurs Python – décorateurs de fermetures et de fonctions

Bien que cette façon de créer une nouvelle fonction ne modifie pas la fonction d'origine, elle se heurte à un problème très sérieux :

ne peut qu'étendre la fonction spécifiée et ne peut pas être utilisé universellement pour d'autres fonctions.Par exemple, la fonction add ci-dessus ne peut pas être étendue, mais la fonction mul ne peut pas être étendue. Si vous souhaitez étendre la fonction mul, vous ne pouvez créer qu'une fonction d'extension

Parce que nous espérons définir ; une fonction d'extension universelle qui peut couvrir toutes les fonctions. Ce type de fonction universelle qui ne change pas le code de fonction d'origine est : décorateur.

2. Décorateur de fonction

Un décorateur est essentiellement une fonction ou une classe Python, qui permet à d'autres fonctions ou classes d'ajouter des fonctions supplémentaires sans apporter de modifications au code, c'est-à-dire en ajoutant des fonctions supplémentaires aux objets existants. le décorateur est aussi 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.

1) La fonction décorée ne prend pas de paramètres

Par exemple :

def wrapper_info(func):
def inner():
print("开始介绍...")
res = func()
print("介绍结束...")
return res

return inner

def introduce1():
print("我是周润发,我来自HONG KONG")

info = wrapper_info(introduce1)
info()
Copier après la connexion

Le résultat d'exécution est le suivant :

Décorateurs Python – décorateurs de fermetures et de fonctions

On peut voir que sans changer le code de fonction d'origine, certaines fonctions supplémentaires sont ajoutées au fonction originale, func C'est la fonction à décorer. Elle est passée dans la fonction de décoration en tant que variable et peut être utilisée universellement pour d'autres fonctions. Ce wrapper_info est le décorateur. Mais le problème actuel est le suivant : que se passe-t-il si la fonction décorée prend des paramètres ? Par exemple :

def introduce2(name, age):
print(f"我叫{name}, 我今年{age}岁了")
Copier après la connexion

2) La fonction décorée a des paramètres

Bien que le nom et l'âge puissent être transmis dans le décorateur wrapper_info, toutes les fonctions décorées n'ont pas uniquement un nom, un âge ou des paramètres d'un type spécifié. dans un dictionnaire, une liste, un tuple, etc. Autrement dit, que dois-je faire si le type et le nombre de paramètres transmis ne sont pas corrigés ?

À ce stade, vous devez utiliser des paramètres de longueur variable : (*args, **kwargs)

def wrapper_info(func):
"""
用来对其他函数进行扩展,使其他函数可以在执行前做一些额外的动作
:param func: 要扩展的函数对象
:return:
"""
def inner(*args, **kwargs):
print("开始介绍...")
res = func(*args, **kwargs)
print("介绍结束...")
return res

return inner
Copier après la connexion

Par exemple :

def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")
Copier après la connexion

Les résultats d'exécution sont les suivants :

Décorateurs Python – décorateurs de fermetures et de fonctions

3)装饰器带参数

上述提到的是装饰器,一种是应用于被装饰的函数不带参数,一种是被装饰的函数带参数,那装饰器本身能否带参数呢?比如我定义一个变量,想通过传入不同的值来控制这个装饰器实现不同的功能。答案是肯定的,例如:

def use_log(level):
def decorator(func):
def inner(*args, **kwargs):
if level == "warn":
logging.warning("%s is running by warning" % func.__name__)
elif level == "info":
logging.warning("%s is running by info" % func.__name__)
else:
logging.warning("%s is running by other" % func.__name__)
return func(*args, **kwargs)

return inner

return decorator


def introduce4(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")


info1 = use_log(introduce4('周星驰', 28, '香港'))
info1('info')
info2 = use_log(introduce4('周润发', 28, '香港'))
info2('warn')
info3 = use_log(introduce4('成龙', 28, '香港'))
info3('xxx')
Copier après la connexion

运行结果如下:

Décorateurs Python – décorateurs de fermetures et de fonctions

3.装饰器调用

方式一:以函数方式调用

info3 = wrapper_info(introduce3)
info3('刘德华', 28, '香港')
Copier après la connexion

如果是装饰器函数带参数,则调用方式为:

info4 = use_log(introduce4('周星驰', 28, '香港'))
info4('info')
Copier après la connexion

方式二:以语法糖方式调用

即在被装饰函数上方以@符号进行修饰

@wrapper_info
def introduce3(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")

introduce3('刘德华', 28, '香港')
Copier après la connexion

如果是装饰器函数带参数,例如上述的use_log,则需要在装饰器中传入参数:

@use_log('info')
def introduce4(name, age, city):
print(f"我叫{name}, 我今年{age}岁了, 我来自{city}")
Copier après la connexion

小结

什么是装饰器?

在不改变原函数代码的情况下,给原函数增加了一些额外的功能,并且能够通用于其他函数,这样的函数就称作为装饰器。

装饰器的调用

可以通过传统调用函数的方式进行调用,也可以通过@装饰器的方式调用

装饰器的特点

  • 通过装饰器,可以在不修改原来函数的情况下对函数进行扩展
  • 一个函数可以同时指定多个装饰器

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!

Étiquettes associées:
source:51cto.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal