in python 3.4+, Ceci donne P> functools.arras code> conserve la signature de la fonction qu'elle enveloppe. Malheureusement, si vous créez des décorateurs destinés à être empilés les uns sur les autres, le second (ou la dernière) décorateur de la séquence constatera le générique
* args code> et
** kwargs Signature du wrapper em> et ne préservant pas la signature de la fonction d'origine jusqu'au bas de la séquence des décorateurs. Voici un exemple.
-------------------------------------------------------------------------
KeyError Traceback (most recent call last)
<ipython-input-5-69c17467332d> in <module>
22
23
---> 24 foo()
<ipython-input-5-69c17467332d> in wrapper(*args, **kwargs)
4 @wraps(func)
5 def wrapper(*args, **kwargs):
----> 6 assert kwargs['x'] <= 2
7 return func(*args, **kwargs)
8 return wrapper
KeyError: 'x'
3 Réponses :
Vous n'êtes pas en train de transmettre des arguments à votre fonction Vous pouvez essayer d'obtenir des arguments de fonction par défaut à l'aide de Inspectez P> p> foo code> donc
* args code> et
** kwargs code> sont vides pour les deux décorateurs. Si vous passez des arguments, les décorateurs fonctionnent juste bien
Je ne sais pas qui a évité cela, mais il est totalement correct et totalement pertinent - même s'il ne suffit pas de résoudre le problème.
Je ne peux pas parler à tout cela, mais comme la personne qui pose la question, je ne vois pas cela aussi utile car la question est très spécifique au cas de ne pas réussir Kwargs à la fonction décorée, et les avoir à la fois référencés À partir des paramètres par défaut de la fonction décorée sans i> Utilisation de Inspect code> (par exemple, s'appuyer sur les enveloppements fait de sorte que dans le corps de l'emballage, vous pouvez accéder aux valeurs par défaut d'origine) .
Vous ne pouvez pas vraiment obtenir les valeurs par défaut sans utiliser ceci imprime p> inspecter code> et vous devez également compter pour positionner les arguments de position (
* args code>) vs mots-clés args (
* * kwargs code>). Donc, normaliser les données si c'est là s'il est manquant, inspectez la fonction
Notez que le décorateur intérieur, tel que @validate_y code> fonctionne simplement dans mon exemple. Il utilise
kwargs code> à partir de la fonction emballée sans aucun
inspecter code> manipulation. L'erreur ne se produit que pour le décorateur externe que
wraps (...) code> une autre fonction décorée.
Lorsque j'ai dirigé votre code dans le débogueur, les arguments et les kwargs étaient vides. En effet, les valeurs par défaut sont remplies au moment où la fonction est appelée non auparavant. C'est pourquoi j'ai ajouté la normalisation de args code> ->
kwargs code> et nécessaire
inspecter code> pour obtenir les valeurs par défaut.
Cela est vrai pour le décorateur extérieur, mais ne semble pas être vrai pour le décorateur intérieur.
oely le décorateur d'intérieur @validate_y code> ne fonctionne pas non plus dans votre exemple; C'est juste que vous n'avez pas donné une chance d'échouer, car
@validate_x code> a d'abord échoué. Le comportement est le même si vous utilisez un ou les deux décorateurs; De toute façon, il soulève un
KeyError code>.
Votre diagnostic est incorrect; En fait, functools.wraps code> conserve la signature de la fonction à double décoration:
def validate_x(func):
@wraps(func)
def wrapper(*args, **kwargs):
if 'x' in kwargs and kwargs['x'] > 2:
raise ValueError('Invalid x, should be <= 2, was ' + str(x))
return func(*args, **kwargs)
return wrapper
@validate_x
def bar(*, x=1): # keyword-only arg, prevent passing as positional arg
...
J'ai déjà abordé cela dans ma question. Notez que inspect.signature code> a un paramètre
suivi_wraped code> VRAI par défaut, mais la signature réelle utilisée dans la fonction Call Machinery est différente, ne suivant pas les wraps.
La signature réelle du wrapper est (* args, ** kwargs) code> car c'est comme ça que vous l'avez dit. Comme je l'ai dit, l'erreur n'est pas dans la signature, mais dans la logique de vos décorateurs. Vous accédez inconditionnellement
kwargs ['x'] code> lorsque
kwargs code> n'est pas garanti de contenir
'x' code> comme une touche. Si la signature était le problème, vous obtiendriez un
typeError code> pour appeler
foo code> avec les mauvais arguments, pas un
KeyError code> du corps de la une fonction; Le fait que le corps de la fonction exécute du tout signifie que les arguments sont acceptables en fonction de la signature.
Pour une analogie, si vous avez défini defr wrapper (* args, ** kwargs): élever ValueError () code> puis
FOO () Code> Souleverait une ValueError car c'est le comportement déclaré de la fonction d'emballage; Le fait que l'appel de la fonction décorée augmente une erreur ne signifie pas que sa signature est fausse.
Mais dans le décorateur intérieur, les références à la plaine kwargs code> sont capables d'accéder à la valeur par défaut transmise à la fonction décorée, sans utilisation de
inspecter code>. Ce n'est que le décorateur externe qui échoue.
kwargs code> serait rempli avec les valeurs par défaut de la fonction emballées, si la fonction Wrapper est appelée sans arguments. Par exemple. Si
wrapper code> wraps
applico code> en utilisant
functools.arras code> et
func code> définit le mot-clé arguments avec des valeurs par défaut, alors il devrait être impossible pour
Wrapper Code> Pour toujours être appelé avec vide
kwargs code>. Il serait toujours i> avoir ceux qui venaient de la fonction qu'elle enveloppe, au minimum, inévitablement.
"Mais dans le décorateur intérieur, les références à des kwargs ordinaires peuvent accéder à la valeur par défaut passée à la fonction décorée, sans utiliser d'inspection. Ce n'est que le décorateur externe qui échoue." I> Ce n'est tout simplement pas vrai; Si vous utilisez uniquement le @validate_y code> décorateur sans
@validate_x code> alors il échoue avec
KeyError: 'y' code>. La seule raison pour laquelle le décorateur intérieur ne soulève pas une erreur lorsque vous utilisez les deux décorateurs, c'est que le décorateur externe soulève une erreur avant d'avoir une chance d'appeler la fonction emballée interne.
J'ai modifié pour ajouter une démonstration que kwargs code> n'est pas rempli avec la valeur par défaut de l'argument lorsque vous n'avez qu'un décorateur.