web-dev-qa-db-fra.com

Quelles sont les utilisations les plus courantes des décorateurs de Python?

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.

325
Dana

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):
    ...
122
RSabet

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.

96
John Fouhy

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.

66
Simon

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 .

47
cdleary

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.

23
DNS

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
23
Torsten Marek

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()
12
MisterMetaphor

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).

8
DzinX

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
6
luc

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())
5
Nikhil Chelliah

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.

5
aatifh

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
1
user1476056

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'

1
HVNSweeting