2013-03-14 7 views
5

ich folgenden Schnipsel habe:Hinzufügen Dynamically Klassenmethoden zu einer Klasse

FEED_TYPES = [ 
    ('fan_mail',  'Fan Mail'), 
    ('review',  'Review'), 
    ('tip',   'Tip'), 
    ('fan_user',  'Fan User'), 
    ('fan_song',  'Fan Song'), 
    ('fan_album', 'Fan Album'), 
    ('played_song', 'Played Song'), 
    ('played_album', 'Played Album'), 
    ('played_radio', 'Played Radio'), 
    ('new_event', 'New Event'), 
] 

class Feed: 
    @classmethod 
    def do_create(cls, **kwargs): 
     print kwargs 

    @classmethod 
    def create(cls, type, **kwargs): 
     kwargs['feed_type'] = type 
     cls.do_create(**kwargs) 

for type_tuple in FEED_TYPES: 
    type, name = type_tuple 

    def notify(self, **kwargs): 
     print "notifying %s" % type 
     self.create(type, **kwargs) 

    notify.__name__ = "notify_%s" % type 
    setattr(Feed, notify.__name__, classmethod(notify)) 

Feed.create("FanMail", to_profile="Gerson", from_profile="Felipe") 
Feed.notify_fan_mail(to_profile="Gerson2", from_profile="Felipe2") 

Die Idee dynamisch eine Klasse ist, Verfahren zu schaffen (wie notify_fan_mail) für jeden Futtersort. Es funktioniert fast super, das einzige Problem ist, dass die Drucken Anweisung immer "Benachrichtigung new_event" druckt, unabhängig von der Methode, die ich anrufe (das gleiche für notify_new_mail, notify_review, etc.).

Ich weiß, dass es ist, weil es den letzten Wert verwendet, der dem Typ zugewiesen wird. Meine Frage ist: Wie kann ich dynamisch Methoden erstellen, die den richtigen Wert für Typ verwenden würden?

Auch, wenn ich genau diesen Code in einer Python-Datei habe, ist das der richtige Weg, Methoden zur Feed-Klasse hinzuzufügen, oder gibt es einen eleganteren Weg?

Antwort

5

einen Verschluss Verwenden Sie den Wert von kind zu erhalten:

for type_tuple in FEED_TYPES: 
    kind, name = type_tuple 
    def make_notify(kind): 
     def notify(self, **kwargs): 
      print "notifying %s" % kind 
      self.create(kind, **kwargs) 
     return notify 
    notify = make_notify(kind) 
    notify.__name__ = "notify_%s" % kind 
    setattr(cls, notify.__name__, classmethod(notify)) 

By the way, nicht type als Variablennamen verwenden, da es die builtin des gleichen Namens Schatten.


Eine elegantere Weise Feed zu ändern ist, eine Klasse Dekorateur zu erstellen. Dies macht es klarer, dass Sie Code haben, der die ursprüngliche Definition von Feed ändert.

FEED_TYPES = [ 
    ('fan_mail',  'Fan Mail'), 
    ('review',  'Review'), 
    ('tip',   'Tip'), 
    ('fan_user',  'Fan User'), 
    ('fan_song',  'Fan Song'), 
    ('fan_album', 'Fan Album'), 
    ('played_song', 'Played Song'), 
    ('played_album', 'Played Album'), 
    ('played_radio', 'Played Radio'), 
    ('new_event', 'New Event'), 
] 

def add_feed_types(cls): 
    for type_tuple in FEED_TYPES: 
     kind, name = type_tuple 
     def make_notify(kind): 
      def notify(self, **kwargs): 
       print "notifying %s" % kind 
       self.create(kind, **kwargs) 
      return notify 
     notify = make_notify(kind) 
     notify.__name__ = "notify_%s" % kind 
     setattr(cls, notify.__name__, classmethod(notify)) 
    return cls 

@add_feed_types 
class Feed: 
    @classmethod 
    def do_create(cls, **kwargs): 
     print kwargs 

    @classmethod 
    def create(cls, kind, **kwargs): 
     kwargs['feed_type'] = kind 
     cls.do_create(**kwargs) 


Feed.create("FanMail", to_profile="Gerson", from_profile="Felipe") 
Feed.notify_fan_mail(to_profile="Gerson2", from_profile="Felipe2") 
+0

Vielen Dank! Die Zeilen 'notify = make_notify (typ)' und 'notify .__ name__ =" notify_% s "% typ' sollten' type' (statt 'typ') verwenden, richtig? – kolrie

+0

Hoppla, 'self.create (type, ...)' sollte 'self.create (typ, ...)' sein.Überall, wo du 'type' geschrieben hast, schlage ich vor, etwas anderes zu verwenden, vielleicht" freundlich ", um es vollständig von dem eingebauten Python zu unterscheiden. – unutbu

+0

Liebe die Klasse Dekorateur Konzept! – kolrie

1

Der Fehler wird durch die Art der Schließungen in Python verursacht. Der Name type in Ihren Benachrichtigungsfunktionen ist im umschließenden Bereich an type gebunden. Wenn Sie den Wert type ändern, ändert sich der Wert für alle darauf bezogenen Schließungen.

Eine Möglichkeit, dies zu lösen, ist eine Funktion Fabrik verwenden:

def make_notify_function(type): 
    def notify(self, **kwargs): 
     print "notifying %s" % type 
     self.create(type, **kwargs) 
    return notify 
1

Das Problem, das Sie in Laufen sind, ist, dass Ihre notify Funktion nicht den Wert ein gekapseltes type, nur Name es ist. Wenn also Ihre for-Schleife auf das nächste Tupel übergeht, ist das alte verloren.

Sie können dieses Problem beheben, indem type ein Standard-Argument an die Funktion machen:

for type, name in FEED_TYPES: # no need to unpack the tuple separately 
    def notify(cls, type=type, **kwargs): # type is an argument and default value 
     print "notyfying %s" % type 
     cls.create(type, **kwargs) 

    ... 

Bitte beachte, dass ich das self Argument cls geändert haben, die wahrscheinlich mehr korrekt ist, da du es machst ein Klassenmethode.

Ich denke, dies ist ein geeigneter Weg, um Methoden zur Laufzeit einer Klasse hinzuzufügen. Ich bin mir nicht sicher, ob das notwendigerweise etwas ist, das Sie tun müssen, aber ohne weitere Informationen über Ihre Aufgabe (zum Beispiel was macht do_create?) Ich sehe keine anderen offensichtlichen Verbesserungen.

+0

Ich denke, das schlägt fehl, wenn die Funktion mit mehr als einem Positionsargument oder einem benannten Argument namens 'type' aufgerufen wird. –