2017-09-21 1 views
2

Je me demande comment je peux installer un décorateur dans une classe qui accepte les variables de classe. L'exemple où je pense que cela pourrait être utile est dans Pyside/PyQt où je dois bloquer et débloquer les signaux sur les widgets au début et à la fin des fonctions.Décore un objet de classe en passant des variables de classe

Exemple:

class Window(QtGui.QMainWindow): 
    .... 

    def updateList(self, *args): 
     self.list.blockSignals(False) 
     //do things on self.list 
     self.list.blockSignals(True) 

Maintenant, il y a le potentiel d'un bon nombre d'endroits sur différents widgets cela pourrait être fait. Bien sûr, je peux faire cette méthode de blocage et de déblocage de chaque élément, mais c'est fastidieux. Et je dois me souvenir de débloquer tout quand j'ai fini.

Passant à une autre étape, je peux déplacer le blocage dans sa propre fonction.

class Window(QtGui.QMainWindow): 
    .... 

    def updateList(self, *args): 
     self.block([self.list]) 
     //do things on self.list 
     self.block([self.list]) 

    def block(self, items): 
     for item in items: 
      if item.signalsBlocked(): 
       item.blockSignals(False) 
      else: 
       item.blockSignals(True) 

Sweet! Je peux pepper cela autour de mon code et me sens très utile. Mais j'ai l'impression qu'il me manque une forme de boss final pour la rendre vraiment utile au niveau mondial. Comme une sorte de décorateur. En utilisant cette réponse, je peux transmettre des variables à mon décorateur et décorer ma fonction! Decorators with parameters?

def block(items): 
    def dec(fn): 
     def wrapped(*args, **kwargs): 
      for item in items: 
       item.blockSignals(True) 
      fn(*args, **kwargs) 
      for item in items: 
       item.blockSignals(False) 
     return wrapped 
    return dec 


class Window(QtGui.QMainWindow): 
    .... 

    @block([self.list]) 
    def updateList(self, *args): 
     //do things on self.list 

Maintenant, je me sens qui pourrait vraiment être utile! Sauf que @block ([self.list]) n'a aucune idée de ce qu'est le soi. Donc, ce que je tente de faire, est-ce raisonnable de supposer que je peux le faire? Est-ce possible, ou suis-je en train de chasser des dragons sauvages? Si c'est possible, quelle est la bonne façon d'essayer cela?

+0

@DavisHerring ah oui, j'ai regardé l'autre réponse à nouveau, je l'ai corrigé. – ooklah

+0

(nettoyage des commentaires ...) –

+0

Cela devrait être un gestionnaire de contexte, plutôt qu'un décorateur. Ce serait beaucoup plus flexible de cette façon, car il ne serait pas lié à un objet ou une fonction/méthode particulière. – ekhumoro

Répondre

2

Vous ne pouvez pas faire référence à l'attribut valeurs au moment de la définition de la classe, mais vous pouvez utiliser leurs noms:

def block(*attrs): 
    def dec(fn): 
     @functools.wraps(fn) 
     def wrap(self,*args,**kwargs): 
      for a in attrs: getattr(self,a).blockSignals(True) 
      ret=fn(self, *args, **kwargs) 
      for a in attrs: getattr(self,a).blockSignals(False) 
      return ret 
     return wrap 
    return dec 

Il est un peu moche d'avoir à écrire

class Window(QtGui.QMainWindow): 
    @block("list1","list2") 
    def updateList(self, *args): # ... 

avec la chaîne de guillemets et tout, mais ça marche.

+0

Je pense que j'aurais utilisé 'instance' comme nom de paramètre au lieu de' self' dans 'wrap' - mais ... – wwii

+0

' instance' entrerait en conflit avec un argument du même nom dans la fonction enveloppée. La fonction 'wrap' deviendra une méthode de la classe dès que le descripteur la renverra,' self' me semble parfaitement idiomatique. –

2

Voici un gestionnaire de contexte simple, à titre de comparaison:

class blocked(object): 
    def __init__(self, *targets): 
     self._targets = targets 

    def __enter__(self): 
     for target in self._targets: 
      target.blockSignals(True) 

    def __exit__(self, cls, exception, traceback): 
     for target in self._targets: 
      target.blockSignals(False) 

et voici comment l'utiliser:

class Window(QtGui.QMainWindow): 
    ... 

    def updateList(self, *args): 
     with blocked(self.list): 
      # do things with self.list 

L'avantage de faire les choses de cette façon est que blocked peut être utilisé partout, et les cibles peuvent être spécifiées dynamiquement. On pourrait aussi argumenter qu'il est plus lisible, puisque c'est self.list (l'émetteur de signal) qui est bloqué, pas (pas l'), pas self.updateList.

+0

Je peux voir immédiatement où cela est utile lorsque les signaux ne doivent pas être bloqués pour toute la fonction. Un décorateur bloque la fonction entière dans ces exemples. – ooklah