2009-05-22 13 views
1

Ich möchte eine einmalige Rückrufregistrierung innerhalb von Observer durchführen. Ich möchte die Registrierung innerhalb von init oder anderen Funktionen nicht machen. Ich weiß nicht, ob es eine Klassenstufe Äquivalent für init istWie referenziere ich eine Klassenmethode außerhalb eines Funktionskörpers in 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 

Antwort

1

ich gekommen bin, dass Python zu akzeptieren, ist nicht sehr intuitiv, wenn es um funktionale Programmierung innerhalb Klassendefinitionen kommt. Siehe this question. Das Problem mit der ersten Methode besteht darin, dass Observer nicht als Namespace existiert, bis die Klasse erstellt wurde. Das Problem mit der zweiten ist, dass Sie eine Klassenmethode erstellt haben, die erst nach der Erstellung des Namespace das tut, was sie eigentlich tun soll. (Ich habe keine Ahnung, warum Sie das dritte versuchen.) In beiden Fällen tritt keines dieser Dinge auf, bis die Klassendefinition von Observer aufgefüllt ist.

Das klingt vielleicht wie eine traurige Einschränkung, aber es ist wirklich nicht so schlimm. Registrieren Sie einfach nach die Klassendefinition. Sobald Sie feststellen, dass es nicht schlecht ist, bestimmte Initialisierungsroutinen für Klassen im Rumpf des Moduls auszuführen, aber außerhalb des Klassenkörpers, wird Python viel freundlicher. Versuchen: Klasse Observer:

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

Aufgrund der Art und Weise Module in Python arbeiten, werden Sie garantiert, dass diese Registrierung einmal durchgeführt werden und nur ein einziges Mal (abgesehen Prozess Forking und vielleicht einige andere irrelevant Grenzfälle), wo Observer ist importiert.

0

oops. Das tut mir leid. Alles, was ich tun musste, war das Abonnement außerhalb der Klassendefinition

class Observer: 

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

#after end of class 

NewUserRegistered().subscribe(Observer.on_new_user_registration) 

Vermutung ist es eine Nebenwirkung von zu viel Java zu bewegen, die man nicht sofort daran denken ist.

+0

Welche Version von Python verwenden Sie? Der Aufruf der Klassenmethode innerhalb der Klasse funktionierte für mich in Python 2.5. –

+0

das ist seltsam. Ich verwende 2.5.1 und Ihr Beispiel unten gibt mir NameError – ottodidakt

+0

Es kann sehr gut ein Fehler in 2.5.1 sein. Ich nehme an, das ist genug, um zu verhindern, dass Sie meine Syntax verwenden. :-) –

0

Was Sie tun sollte funktionieren:

>>> 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! 

Eine Sache allerdings zu beachten, nehmen Klassenmethoden ein cls Argument statt eines Selbst Argument. So sollte Ihre Klassendefinition wie folgt aussehen:

class Observer: 

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

ja. Danke für diese Korrektur über cls – ottodidakt

+0

Aber beachte, dass sowohl "self" als auch "cls" völlig willkürlich sind - sie sind nur (sehr starke) Konventionen. –

+0

In der Tat. Ich würde empfehlen, so zu tun, als wären sie sprachlich durchgesetzt. Es gibt sehr wenig Grund, dies nicht zu tun. –

2

ich die Anzahl der Klassen zu reduzieren vorschlagen - denken Sie daran, dass Python ist nicht Java. Jedes Mal, wenn Sie @classmethod oder @staticmethod verwenden, sollten Sie aufhören und darüber nachdenken, da diese Schlüsselwörter in Python ziemlich selten sind.

es so tun funktioniert:

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

Wenn Sie eine Observer-Klasse wollen, dann können Sie es wie folgt tun:

Klasse Observer:

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

Aber beachten Sie, dass die Die statische Methode hat keinen Zugriff auf die Protokollinstanz. Dies ist wahrscheinlich nicht sehr hilfreich. Sie können eine Methode, die an eine Objektinstanz gebunden ist, nicht abonnieren, da das Objekt nicht existiert, wenn die Klassendefinition ausgeführt wird.

Aber man kann dies natürlich tun:

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) 

, wo wir die gebundenen o.on_new_user_registration als Argument abonnieren verwenden.

Verwandte Themen