J'ai un problème avec le transfert de la variable 'insurance_mode' par le décorateur. Je le ferais par la déclaration de décorateur suivante:
@execute_complete_reservation(True)
def test_booking_gta_object(self):
self.test_select_gta_object()
mais malheureusement, cette déclaration ne fonctionne pas. Peut-être existe-t-il une meilleure façon de résoudre ce problème.
def execute_complete_reservation(test_case,insurance_mode):
def inner_function(self,*args,**kwargs):
self.test_create_qsf_query()
test_case(self,*args,**kwargs)
self.test_select_room_option()
if insurance_mode:
self.test_accept_insurance_crosseling()
else:
self.test_decline_insurance_crosseling()
self.test_configure_pax_details()
self.test_configure_payer_details
return inner_function
execute_complete_reservation
prend deux paramètres, mais vous en passez un. Les décorateurs ne sont que du sucre syntaxique pour encapsuler des fonctions à l'intérieur d'autres fonctions. Voir docs.python.org/reference/compound_stmts.html#function pour une documentation complète.Réponses:
La syntaxe des décorateurs avec des arguments est un peu différente - le décorateur avec des arguments doit retourner une fonction qui prendra une fonction et retournera une autre fonction. Il devrait donc vraiment rendre un décorateur normal. Un peu déroutant, non? Ce que je veux dire est:
Ici, vous pouvez en savoir plus sur le sujet - il est également possible de l'implémenter à l'aide d'objets appelables et cela est également expliqué ici.
la source
return function(*args, **kwargs)
@decorator()
et pas seulement@decorator
, même si vous n'avez que des arguments optionnels.Edit : pour une compréhension approfondie du modèle mental des décorateurs, jetez un œil à ce génial Pycon Talk. vaut bien les 30 minutes.
Une façon de penser les décorateurs avec des arguments est
Se traduit par
Donc, si le décorateur avait des arguments,
Se traduit par
decorator_with_args
est une fonction qui accepte un argument personnalisé et qui renvoie le décorateur réel (qui sera appliqué à la fonction décorée).J'utilise une astuce simple avec des partiels pour rendre mes décorateurs faciles
Mise à jour:
Ci-dessus,
foo
devientreal_decorator(foo)
Un effet de la décoration d'une fonction est que le nom
foo
est remplacé lors de la déclaration du décorateur.foo
est "remplacé" par tout ce qui est retourné parreal_decorator
. Dans ce cas, un nouvel objet fonction.Toutes
foo
les métadonnées de sont remplacées, notamment docstring et nom de fonction.functools.wraps nous donne une méthode pratique pour "soulever" la docstring et le nom à la fonction retournée.
la source
@functools.wraps
?functool.wraps
. L'ajouter dans l'exemple peut embrouiller davantage les lecteurs.arg
ici!?bar
à l'argument dereal_decorator
?Je voudrais montrer une idée qui est à mon humble avis assez élégante. La solution proposée par t.dubrownik présente un motif qui est toujours le même: vous avez besoin de l'emballage à trois couches indépendamment de ce que fait le décorateur.
J'ai donc pensé que c'était un travail pour un méta-décorateur, c'est-à-dire un décorateur pour les décorateurs. Comme un décorateur est une fonction, il fonctionne en fait comme un décorateur ordinaire avec des arguments:
Cela peut être appliqué à un décorateur ordinaire afin d'ajouter des paramètres. Par exemple, disons que nous avons le décorateur qui double le résultat d'une fonction:
Avec
@parametrized
nous pouvons construire un@multiply
décorateur générique ayant un paramètreClassiquement, le premier paramètre d'un décorateur paramétré est la fonction, tandis que les arguments restants correspondront au paramètre du décorateur paramétré.
Un exemple d'utilisation intéressant pourrait être un décorateur assertif de type sécurisé:
Une dernière note: ici, je n'utilise pas
functools.wraps
les fonctions wrapper, mais je recommanderais de l'utiliser tout le temps.la source
@wraps
mien pour mon cas particulier.@parametrized
astuce. Le problème que j'ai eu, c'est que j'ai oublié que la@
syntaxe est égale aux appels réels (en quelque sorte, je le savais et je ne le savais pas en même temps que vous pouvez le comprendre à partir de ma question). Donc, si vous voulez traduire la@
syntaxe en appels banals pour vérifier comment cela fonctionne, vous feriez mieux de la commenter temporairement d'abord ou vous finiriez par l'appeler deux fois et obtenir des résultatsVoici une version légèrement modifiée de la réponse de t.dubrownik . Pourquoi?
Alors utilisez
@functools.wraps()
:la source
Je suppose que votre problème consiste à transmettre des arguments à votre décorateur. C'est un peu délicat et pas simple.
Voici un exemple de la façon de procéder:
Tirages:
Voir l'article de Bruce Eckel pour plus de détails.
la source
__name__
lesquelles une instance de la classe décorateur n'aura pas?class Foo: @MyDec(...) def method(self, ...): blah
auquel je faisais référence ne fonctionnait pas carFoo().method
il ne s'agirait pas d'une méthode liée et ne passerait pasself
automatiquement. Cela aussi peut être corrigé, en créantMyDec
un descripteur et en créant des méthodes liées__get__
, mais c'est plus compliqué et beaucoup moins évident. En fin de compte, les cours de décoration ne sont pas aussi pratiques qu'ils le semblent.Utilisation du décorateur
Puis le
produit
mais
produit
la source
Ceci est un modèle pour un décorateur de fonction qui ne nécessite pas
()
si aucun paramètre ne doit être donné:un exemple de ceci est donné ci-dessous:
la source
factor_or_func
(ou tout autre paramètre) ne doit jamais se fait réaffectés danswrapper()
.locals()
?()
.Dans mon cas, j'ai décidé de résoudre ce problème via un lambda à une ligne pour créer une nouvelle fonction de décorateur:
Une fois exécuté, il affiche:
Peut-être pas aussi extensible que d'autres solutions, mais cela a fonctionné pour moi.
la source
Écrire un décorateur qui fonctionne avec et sans paramètre est un défi car Python attend un comportement complètement différent dans ces deux cas! De nombreuses réponses ont essayé de contourner cela et ci-dessous est une amélioration de la réponse par @ norok2. Plus précisément, cette variation élimine l'utilisation de
locals()
.En suivant le même exemple que celui donné par @ norok2:
Jouez avec ce code .
Le hic est que l'utilisateur doit fournir des paires de paramètres clés et valeurs au lieu de paramètres positionnels et le premier paramètre est réservé.
la source
Il est bien connu que les deux morceaux de code suivants sont presque équivalents:
Une erreur courante est de penser que
@
cache simplement l'argument le plus à gauche.Il serait beaucoup plus facile d'écrire des décorateurs si ce qui précède est comme cela a
@
fonctionné. Malheureusement, ce n'est pas ainsi que les choses se font.Considérons un décorateur
Wait
qui interrompt l'exécution du programme pendant quelques secondes. Si vous ne passez pas de temps d'attente, la valeur par défaut est 1 seconde. Les cas d'utilisation sont indiqués ci-dessous.Quand
Wait
a un argument, tel que@Wait(3)
, alors l'appelWait(3)
est exécuté avant qu'il ne se passe quoi que ce soit d'autre.Autrement dit, les deux morceaux de code suivants sont équivalents
C'est un problème.
Une solution est présentée ci-dessous:
Commençons par créer la classe suivante
DelayedDecorator
:Maintenant, nous pouvons écrire des choses comme:
Notez que:
dec
n'accepte pas plusieurs arguments.dec
accepte uniquement la fonction à encapsuler.import inspect classe PolyArgDecoratorMeta (type): def call (Wait, * args, ** kwargs): try: arg_count = len (args) if (arg_count == 1): if callable (args [0]): SuperClass = inspect. getmro (PolyArgDecoratorMeta) [1] r = SuperClass. call (Wait, args [0]) else: r = DelayedDecorator (Wait, * args, ** kwargs) else: r = DelayedDecorator (Wait, * args, ** kwargs) enfin: pass return r
importation de la classe de temps Wait (métaclasse = PolyArgDecoratorMeta): def init (i, func, delay = 2): i._func = func i._delay = delay
Les deux morceaux de code suivants sont équivalents:
Nous pouvons imprimer
"something"
sur la console très lentement, comme suit:Notes finales
Cela peut ressembler à beaucoup de code, mais vous n'avez pas à écrire les classes
DelayedDecorator
et àPolyArgDecoratorMeta
chaque fois. Le seul code que vous devez écrire personnellement comme suit, qui est assez court:la source
définir cette "fonction décorateur" pour générer une fonction décoratrice personnalisée:
utilisez-le de cette façon:
la source
Excellentes réponses ci-dessus. Celui-ci illustre également
@wraps
, qui prend la chaîne de doc et le nom de la fonction de la fonction d'origine et l'applique à la nouvelle version encapsulée:Tirages:
la source
Dans le cas où la fonction et le décorateur doivent prendre des arguments, vous pouvez suivre l'approche ci-dessous.
Par exemple, il y a un décorateur nommé
decorator1
qui prend un argumentMaintenant, si l'
decorator1
argument doit être dynamique ou passé lors de l'appel de la fonction,Dans le code ci-dessus
seconds
est l'argument pourdecorator1
a, b
sont les arguments defunc1
la source