2009-05-22 5 views
1

Je souhaite effectuer un enregistrement de rappel unique dans Observer. Je ne veux pas faire l'enregistrement dans init ou dans une autre fonction. Je ne sais pas s'il y a un niveau de classe équivalente initComment faire référence à une méthode de classe en dehors d'un corps de fonction en Python?

class Observer: 

     @classmethod 
     def on_new_user_registration(new_user): 
      #body of handler... 

     # first I try 

     NewUserRegistered().subscribe \ 
      (Observer.on_new_user_registration) #gives NameError for Observer 

     #so I try 

     NewUserRegistered().subscribe(on_new_user_registration) #says not callable 

     #neither does this work 

     NewUserRegistered().subscribe(__metaclass__.on_new_user_registration) 


class BaseEvent(object): 
    _subscriptions = {} 

    def __init__(self, event_info = None): 
     self.info = event_info 

    def fire(self): 
     for callback in self._subscriptions[event_type]: 
      callback(event_info) 

    def subscribe(self, callback): 
     if not callable(callback): 
      raise Exception(str(callback) + 'is not callable') 
     existing = self._subscriptions.get(self.__class__, None) 
     if not existing: 
      existing = set() 
      self._subscriptions[self.__class__] = existing 
     existing.add(callback) 

    class NewUserRegistered(BaseEvent): 
     pass 

Répondre

1

Je suis venu à accepter que python est pas très intuitive en matière de programmation fonctionnelle dans les définitions de classe. Voir this question. Le problème de la première méthode est qu'Observer n'existe pas en tant qu'espace de noms tant que la classe n'a pas été créée. Le problème avec la seconde est que vous avez fait une méthode de classe qui ne fait vraiment ce qu'elle est censée faire qu'après que l'espace de noms ait été créé. (Je ne sais pas pourquoi vous essayez le troisième.) Dans les deux cas, aucune de ces choses ne se produit avant que la définition de classe d'Observateur ait été remplie.

Cela peut sembler une contrainte triste, mais ce n'est vraiment pas si mal. Inscrivez simplement après la définition de classe. Une fois que vous réalisez que ce n'est pas un mauvais style d'effectuer certaines routines d'initialisation sur les classes dans le corps du module mais en dehors du corps de la classe, python devient beaucoup plus convivial. Essayez: Observer classe:

# Define the other classes first 

class Observer: 
    @classmethod 
    def on_new_user_registration(new_user): 
     #body of handler... 
NewUserRegistered().subscribe(Observer.on_new_user_registration) 

En raison des modules façon de travailler en python, vous êtes assuré que cette inscription ne sera effectuée qu'une seule fois (processus barrant fork et peut-être d'autres cas limites non pertinentes) où observateur est importé.

0

oups. Désolé pour ça. Tout ce que je devais faire était de déplacer l'abonnement en dehors de la définition de la classe

class Observer: 

     @classmethod 
     def on_new_user_registration(new_user): 
      #body of handler... 

#after end of class 

NewUserRegistered().subscribe(Observer.on_new_user_registration) 

Je suppose qu'il est un effet secondaire de trop Java que l'on ne pense pas tout de suite de cela.

+0

Quelle version de Python utilisez-vous? Appeler la méthode de classe à l'intérieur de la classe a fonctionné pour moi dans Python 2.5. –

+0

c'est étrange. J'utilise 2.5.1 et votre exemple ci-dessous me donne NameError – ottodidakt

+0

Il peut très bien être un bug dans 2.5.1. Je suppose que cela suffit à vous empêcher d'utiliser ma syntaxe. :-) –

0

Ce que vous faites devrait fonctionner:

>>> class foo: 
...  @classmethod 
...  def func(cls): 
...    print 'func called!' 
... 
>>> foo.func() 
func called! 
>>> class foo: 
...  @classmethod 
...  def func(cls): 
...    print 'func called!' 
...  foo.func() 
... 
func called! 

Une chose à noter cependant, les méthodes de classe prennent un argument cls au lieu d'un argument auto. Ainsi, votre définition de la classe devrait ressembler à ceci:

class Observer: 

    @classmethod 
    def on_new_user_registration(cls, new_user): 
     #body of handler... 
+0

ouais. merci pour cette correction sur les cls – ottodidakt

+0

Mais notez que 'self' et' cls' sont complètement arbitraires - ce ne sont que des conventions (très fortes). –

+0

En effet. Je recommanderais de prétendre qu'ils sont appliqués par la langue. Il y a très peu de raisons de ne pas le faire. –

2

Je suggère de réduire le nombre de classes - rappelez-vous que Python est pas Java. Chaque fois que vous utilisez @classmethod ou @staticmethod vous devriez vous arrêter et y réfléchir puisque ces mots-clés sont assez rares en Python.

Faire comme ça fonctionne:

class BaseEvent(object): 
    def __init__(self, event_info=None): 
     self._subscriptions = set() 
     self.info = event_info 

    def fire(self, data): 
     for callback in self._subscriptions: 
      callback(self.info, data) 

    def subscribe(self, callback): 
     if not callable(callback): 
      raise ValueError("%r is not callable" % callback) 
     self._subscriptions.add(callback) 
     return callback 

new_user = BaseEvent() 

@new_user.subscribe 
def on_new_user_registration(info, username): 
    print "new user: %s" % username 

new_user.fire("Martin") 

Si vous voulez une classe d'observateur, alors vous pouvez le faire comme ceci:

Observer classe:

@staticmethod 
@new_user.subscribe 
def on_new_user_registration(info, username): 
    print "new user: %s" % username 

Mais notez que la La méthode statique n'a pas accès à l'instance de protocole, ce qui n'est probablement pas très utile. Vous ne pouvez pas souscrire à une méthode liée à une instance d'objet comme celle-ci puisque l'objet n'existe pas lors de l'exécution de la définition de classe.

Mais vous pouvez bien sûr faire:

class Observer: 
    def on_new_user_registration(self, info, username): 
     print "new user: %s" % username 

o = Observer() 
new_user.subscribe(o.on_new_user_registration) 

où nous utilisons le o.on_new_user_registration lié comme argument pour vous abonner.

Questions connexes