2013-03-14 78 views
5

我有下面的代碼片斷:動態添加類方法對類

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

的想法是動態地創建一個類方法(如notify_fan_mail)對於每個進料的類型。它的工作原理差不多大,唯一的問題是,打印語句總是打印「通知new_event」,不論何種方法我稱之爲(同爲notify_new_mailnotify_review等)。

我意識到這是因爲它使用了分配給類型的最後一個值。我的問題是:我如何動態創建方法使用類型的正確值?

另外,如果我在Python文件中有這個確切的代碼,那是將方法添加到Feed類的正確方法,還是有更優雅的方法?

回答

5

使用封閉保存的kind值:

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

順便提一下,因爲它陰影同名的內建不使用type作爲變量名。


更好的方法來修改Feed是創建一個類裝飾器。這樣可以更清楚地知道,您的代碼修改了Feed的原始定義。

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

謝謝! 'notify = make_notify(typ)'和'notify .__ name__ ='notify_%s'%typ'行應該使用'type'(而不是'typ'),對嗎? – kolrie 2013-03-14 22:17:58

+0

糟糕,'self.create(type,...)'應該是'self.create(typ,...)'。在你寫'type'的地方,我建議使用不同的東西,也許'kind',以便將它與Python內建完全區分開來。 – unutbu 2013-03-14 22:22:42

+0

喜歡類裝飾者的概念! – kolrie 2013-03-14 23:01:24

1

這個錯誤是由Python中閉包的性質造成的。您的通知功能中的名稱type在封閉範圍內綁定到type。當你改變type的值時,它會改變所有關閉它的閉包。要解決這個

的一種方法是使用函數工廠:

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

你正在運行到的問題是,你notify功能未封裝的價值type,只是它的名字。所以當你的循環進入下一個元組時,舊的會丟失。

您可以通過type默認參數的函數解決這個問題:

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) 

    ... 

請注意,我已經改變了self參數cls,這可能是比較正確的,因爲你讓一個類方法。

我認爲這是在運行時將方法添加到類中的適當方式。我不確定這是否是您需要做的事情,但沒有關於您的任務的更多信息(例如,do_create做什麼?)我沒有看到任何其他明顯的改進。

+0

我認爲如果使用多個位置參數或稱爲'type'的命名參數調用該函數,則會失敗。 – 2013-03-14 22:11:08