Bien que j'aime me considérer comme un codeur raisonnablement compétent Python), les décorateurs sont un aspect de la langue que je n'ai jamais pu maîtriser.
Je sais ce qu’ils sont (superficiellement), j’ai lu des tutoriels, des exemples, des questions sur Stack Overflow, et je comprends la syntaxe, je peux écrire le mien, utiliser occasionnellement @classmethod et @staticmethod, mais il ne m'est jamais venu à l'idée d'utiliser un décorateur pour résoudre un problème dans mon propre Python. Je ne rencontre jamais un problème où je pense, "Hmm ... cela ressemble à un travail pour un décorateur!"
Alors, je me demandais si vous, les gars, pourriez donner quelques exemples de cas où vous avez utilisé des décoratrices dans vos propres programmes, et j'espère avoir un "A-ha!" moment et obtenir eux.
J'utilise des décorateurs principalement à des fins de chronométrage
def time_dec(func):
def wrapper(*arg):
t = time.clock()
res = func(*arg)
print func.func_name, time.clock()-t
return res
return wrapper
@time_dec
def myFunction(n):
...
Je les ai utilisés pour la synchronisation.
import functools
def synchronized(lock):
""" Synchronization decorator """
def wrap(f):
@functools.wraps(f)
def newFunction(*args, **kw):
lock.acquire()
try:
return f(*args, **kw)
finally:
lock.release()
return newFunction
return wrap
Comme indiqué dans les commentaires, puisque Python 2.5, vous pouvez utiliser une instruction with
conjointement avec un threading.Lock
(ou multiprocessing.Lock
depuis la version 2.6) simplifie la mise en oeuvre du décorateur:
import functools
def synchronized(lock):
""" Synchronization decorator """
def wrap(f):
@functools.wraps(f)
def newFunction(*args, **kw):
with lock:
return f(*args, **kw)
return newFunction
return wrap
Peu importe, vous l'utilisez alors comme ceci:
import threading
lock = threading.Lock()
@synchronized(lock)
def do_something():
# etc
@synchronzied(lock)
def do_something_else():
# etc
En gros, il ne fait que mettre lock.acquire()
/lock.release()
de part et d'autre de l'appel de fonction.
J'utilise des décorateurs pour les paramètres de vérification de type qui sont transmis à mes méthodes Python via une interface RMI. Donc, au lieu de répéter le même comptage de paramètres, le mumbo-jumbo déclenche des exceptions encore et encore
def myMethod(ID, name):
if not (myIsType(ID, 'uint') and myIsType(name, 'utf8string')):
raise BlaBlaException() ...
Je viens de déclarer
@accepts(uint, utf8string)
def myMethod(ID, name):
...
et accepte () fait tout le travail pour moi.
Les décorateurs sont utilisés pour tout ce que vous souhaitez "emballer" de manière transparente avec des fonctionnalités supplémentaires.
Django les utilise pour le wrapping fonctionnalité "login requis" sur les fonctions d'affichage , ainsi que pour enregistrement des fonctions de filtrage .
Vous pouvez utiliser les décorateurs de classe pour ajouter des journaux nommés aux classes .
Toute fonctionnalité suffisamment générique que vous pouvez "coller" au comportement d'une classe ou d'une fonction existante constitue un jeu équitable pour la décoration.
Il existe également un discussion sur les cas d'utilisation du groupe de discussion Python-Dev signalé par PEP 318 - Décorateurs pour fonctions et méthodes .
La bibliothèque Twisted utilise des décorateurs combinés à des générateurs pour donner l’illusion qu’une fonction asynchrone est synchrone. Par exemple:
@inlineCallbacks
def asyncf():
doStuff()
yield someAsynchronousCall()
doStuff()
yield someAsynchronousCall()
doStuff()
En utilisant cela, le code qui aurait été divisé en une tonne de petites fonctions de rappel peut être écrit tout à fait naturellement en un seul bloc, le rendant beaucoup plus facile à comprendre et à maintenir.
Pour nos tests, vous pouvez écrire un décorateur qui fournit une fonction ou une méthode de test unitaire avec plusieurs jeux de paramètres:
@parameters(
(2, 4, 6),
(5, 6, 11),
)
def test_add(a, b, expected):
assert a + b == expected
Une utilisation évidente est la journalisation, bien sûr:
import functools
def log(logger, level='info'):
def log_decorator(fn):
@functools.wraps(fn)
def wrapper(*a, **kwa):
getattr(logger, level)(fn.__name__)
return fn(*a, **kwa)
return wrapper
return log_decorator
# later that day ...
@log(logging.getLogger('main'), level='warning')
def potentially_dangerous_function(times):
for _ in xrange(times): rockets.get_rocket(NUCLEAR=True).fire()
Je les utilise principalement pour le débogage (encapsulation d'une fonction qui affiche ses arguments et son résultat) et la vérification (par exemple, pour vérifier si un argument est de type correct ou, dans le cas d'une application Web, si l'utilisateur dispose de privilèges suffisants pour appeler un particulier. méthode).
J'utilise le décorateur suivant pour créer une fonction threadsafe. Cela rend le code plus lisible. Il est presque similaire à celui proposé par John Fouhy mais la différence est que l’on travaille sur une seule fonction et qu’il n’est pas nécessaire de créer un objet de verrouillage de manière explicite.
def threadsafe_function(fn):
"""decorator making sure that the decorated function is thread safe"""
lock = threading.Lock()
def new(*args, **kwargs):
lock.acquire()
try:
r = fn(*args, **kwargs)
except Exception as e:
raise e
finally:
lock.release()
return r
return new
class X:
var = 0
@threadsafe_function
def inc_var(self):
X.var += 1
return X.var
Les décorateurs sont utilisés soit pour définir les propriétés d'une fonction, soit comme passe-partout qui la modifie; il est possible mais contre-intuitif pour eux de renvoyer des fonctions complètement différentes. En regardant les autres réponses ici, il semble que l’une des utilisations les plus courantes est de limiter la portée d’un autre processus - qu’il s’agisse de la journalisation, du profilage, des contrôles de sécurité, etc.
CherryPy utilise la répartition d'objets pour faire correspondre les URL aux objets et, éventuellement, aux méthodes. Les décorateurs de ces méthodes indiquent si CherryPy est ou non autorisé à utiliser ces méthodes. Par exemple, adapté de le tutoriel :
class HelloWorld:
...
def secret(self):
return "You shouldn't be here."
@cherrypy.expose
def index(self):
return "Hello world!"
cherrypy.quickstart(HelloWorld())
Je les ai utilisés récemment, alors que je travaillais sur une application Web de réseautage social. Pour la communauté/les groupes, je devais autoriser les membres à créer une nouvelle discussion et à répondre à un message. Vous devez être membre de ce groupe particulier. Donc, j'ai écrit un décorateur @membership_required
et mettez cela là où je le souhaitais.
Decorator peut être utilisé pour créer facilement des variables de méthode de fonction.
def static_var(varname, value):
'''
Decorator to create a static variable for the specified function
@param varname: static variable name
@param value: initial value for the variable
'''
def decorate(func):
setattr(func, varname, value)
return func
return decorate
@static_var("count", 0)
def mainCallCount():
mainCallCount.count += 1
J'utilise ce décorateur pour fixer un paramètre
def fill_it(arg):
if isinstance(arg, int):
return "wan" + str(arg)
else:
try:
# number present as string
if str(int(arg)) == arg:
return "wan" + arg
else:
# This should never happened
raise Exception("I dont know this " + arg)
print "What arg?"
except ValueError, e:
return arg
def fill_wanname(func):
def wrapper(arg):
filled = fill_it(arg)
return func(filled)
return wrapper
@fill_wanname
def get_iface_of(wanname):
global __iface_config__
return __iface_config__[wanname]['iface']
ceci écrit quand je refacture certaines fonctions ont besoin de passer l'argument "wanN" mais dans mes anciens codes, je passais seulement N ou 'N'