Faire des décorateurs avec des arguments optionnels
from functools import wraps
def foo_register(method_name=None):
"""Does stuff."""
def decorator(method):
if method_name is None:
method.gw_method = method.__name__
else:
method.gw_method = method_name
@wraps(method)
def wrapper(*args, **kwargs):
method(*args, **kwargs)
return wrapper
return decorator
Exemple: L'exemple suivant décore my_function
avec foo_register
au lieu de toujours le faire à decorator
.
@foo_register
def my_function():
print('hi...')
Exemple: L'exemple suivant fonctionne comme prévu.
@foo_register('say_hi')
def my_function():
print('hi...')
Si je veux qu'il fonctionne correctement dans les deux applications (une à l'aide de method.__name__
et le passage le nom), je dois vérifier à l'intérieur de foo_register
pour voir si le premier argument est un décorateur, et si oui, j'ai: return decorator(method_name)
(au lieu de return decorator
). Cette sorte de "check pour voir si c'est un callable" semble très hackish. Est-il une plus belle façon de créer un multi-usage décorateur, comme cette?
P. S. je sais déjà que je peux exiger le décorateur d'être appelé, mais ce n'est pas une "solution". Je veux l'API pour une sensation naturelle. Ma femme aime la décoration, et je ne veux pas gâcher ça.
- C'est la réponse. La fonction doit être soit une fonction décorateur, ou une fonction qui retourne une fonction décorateur, pas par magie l'un ou l'autre selon ses arguments.
- mais sa femme aime à décorer. Et c'est un défi intéressant.
- Je n'ai eu qu'à ajouter deux lignes de code (en réponse ci-dessous), ce qui équivaut à environ 180 octets ou de l'épargne. Cela signifie que je n'ai pas à acheter un nouveau disque dur, afin que ma femme peut conserver décoration.
Vous devez vous connecter pour publier un commentaire.
Le plus propre que je connaisse pour ce faire est la suivante:
Explication
Lorsque le décorateur est appelée sans arguments optionnels comme ceci:
La fonction est passé comme premier argument et décorer retourne le décoré de la fonction, comme prévu.
Si le décorateur est appelée avec un ou plusieurs arguments optionnels comme ceci:
Puis décorateur est appelée avec l'argument de fonction avec la valeur None, donc une fonction qui orne est retourné, comme prévu.
Python 3
Noter que le décorateur la signature ci-dessus peut être améliorée avec Python 3-
*,
syntaxe pour faire respecter l'utilisation sécuritaire de mot-clé arguments. Il suffit de remplacer la signature de l'extérieur de la fonction avec:*
à votre définition de la fonction. E. g.,def decorator(original_function=None, *, argument1=None, argument2=None, ...):
Grâce à l'aide des réponses ici et d'ailleurs, et un tas d'essais et d'erreurs, j'ai trouvé qu'il y est en fait un beaucoup plus facile et la méthode générique permettant de faire des décorateurs prendre des arguments optionnels. Il ne vérifie pas les arguments qu'il a été appelé avec, mais il n'y a pas d'autre moyen de le faire.
La clé est de décorer votre décorateur.
Générique décorateur décorateur code
Voici le décorateur le décorateur (ce code est générique et peut être utilisé par toute personne qui a besoin d'une option arg décorateur):
Utilisation
Son utilisation est aussi simple que:
optional_arg_decorator
Exemple:
Cas de Test
Pour votre cas d'utilisation:
Donc pour votre cas, pour enregistrer un attribut de la fonction avec le passé-le nom de la méthode ou de la
__name__
si Aucun:Ajouter décorées méthodes
Maintenant, vous avez un décorateur qui est utilisable avec ou sans args:
callable(args[0])
retourneTrue
dans les deux cas. Lorsque vous êtes à la décoration de la fonction ET quand vous êtes de l'appeler.Glenn - j'ai dû le faire alors. Je suppose que je suis content qu'il n'y est pas "magique" de la façon de le faire. Je déteste ces.
Donc, voici ma propre réponse (noms de méthode différente de ci-dessus, mais même concept):
Exemple d'utilisation (les deux versions fonctionnent de la même):
Comment sur
Amélioré Générique Décorateur Décorateur Code
Voici mon adaptation de @Nicole répondre avec les améliorations suivantes:
is_bound_method
donnerait un faux positif si le premier argument passé à votre fonction libre avait une propriété de même nom que la fonction.Maintenant que ce vieux thread est de retour au sommet de toute façon, crois-moi juste le jeter dans certains Décorateur-ception:
Maintenant votre magique décorateur est juste une seule ligne!
Par la voie, ce qui fonctionne pour tout le décorateur. Il vient de causes
@foo
de se comporter (aussi près que possible) comme@foo()
.type
est appelable. Envisager d'ajouterand not (type(args[0]) == type and issubclass(args[0], Exception))
à la condition, dans le cas où votre décorateur prend des Exceptions comme arguments (comme ce n').Un générique décorateur pour la décoration décorateur définitions, exprimant qu'décorées décorateur accepte des arguments par défaut, qui sont si aucun n'est explicitement donné.
Il peut être utilisé dans de manières.
Si vous souhaitez cette fonctionnalité sur plusieurs décorateurs vous pouvez échapper le code passe-partout avec un décorateur décorateur:
Remarque: il a l'inconvénient où vous ne pouvez pas passer le 1er argument de la fonction de décorateur.
Note2: si vous avez des conseils/remarques sur la façon d'améliorer cette décorateur, vous pouvez commenter à l'examen du code: https://codereview.stackexchange.com/questions/78829/python-decorator-for-optional-arguments-decorator
J'ai été très agacé par cette question et par la suite écrit une bibliothèque pour le résoudre: décopatch.
Il prend en charge deux styles de développement: imbriquée (comme en python décorateur usines) et plat (un de moins de niveau d'imbrication). C'est comment votre exemple serait mis en œuvre en mode plat:
Noter que j'utilise makefun.enveloppements au lieu de
functools.wraps
ici, de sorte que la signature est entièrement préservée (l'emballage n'est pas appelée du tout si les arguments ne sont pas valides).decopatch
prend en charge un développement supplémentaire de style, que j'appelle double-plat, qui est dédié à la création de signature-la préservation de la fonction des wrappers comme celui-ci. Votre exemple serait mis en œuvre comme ceci:Noter que dans ce style, l'ensemble de votre code est exécuté dans les appels à
method
. Cela pourrait ne pas être souhaitable - que vous pourriez souhaiter pour effectuer les choses une fois à la décoration de temps seulement - pour cela le style précédent serait mieux.Vous pouvez vérifier que les deux styles de travail:
Veuillez vérifier les la documentation pour plus de détails.
Voici une autre variation, ce qui est assez concis et de ne pas utiliser functools:
Selon que
inner_decorator
peut être appelée avec un seul paramètre, on peut alors faire@decorator
,@decorator()
,@decorator(24)
etc.Cela peut être généralisée à un "décorateur décorateur':
Voici une autre solution que de travail aussi, si l'argument optionnel est un callable:
De cette façon, la syntaxe de travail:
Voici ma solution, écrit pour python3. Il a une approche différente des autres puisqu'il définit un rachetables de classe plutôt qu'une fonction.
Vous avez encore appeler explicitement le décorateur
J'ai fait un package simple pour résoudre le problème
Installation
Branche Master
pip install git+https://github.com/ferrine/biwrap
Dernière version
pip install biwrap
Aperçu
Certains wrappers peut avoir des arguments optionnels et souvent, nous voulons éviter de
@wrapper()
appels et de les utiliser@wrapper
à la place.Cela fonctionne pour un simple wrapper
Défini wrapper peut être utilisé dans les deux sens
biwrap
fonctionne également pour les méthodes liéesLes méthodes de la classe /propriétés sont prises en charge trop
Fonction comme call est OK aussi
Une solution similaire à l'instar de ceux de la vérification du type et de la longueur des arguments à l'aide de appelable classes