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等也是如此)。

我意识到这是因为它使用了分配给类型的最后一个值。我的问题是:如何动态创建可以使用正确值的方法type

另外,如果我在 Python 文件中有这个确切的代码,那是向 Feed 类添加方法的正确方法,还是有更优雅的方法?

4

3 回答 3

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")
于 2013-03-14T22:08:48.343 回答
1

该错误是由 Python 中的闭包性质引起的。type通知函数中的名称绑定type在封闭范围内。当您更改type' 值时,所有引用它的闭包都会更改。

解决这个问题的一种方法是使用函数工厂:

def make_notify_function(type):
    def notify(self, **kwargs):
        print "notifying %s" % type
        self.create(type, **kwargs)
    return notify
于 2013-03-14T22:09:03.200 回答
1

您遇到的问题是您的notify函数没有封装 value type,只是它的名称。因此,当您的 for 循环进入下一个元组时,旧的会丢失。

您可以通过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什么?)我看不到任何其他明显的改进。

于 2013-03-14T22:09:05.340 回答