[英]Dynamically adding class methods to a class
I have the following snippet: 我有以下代码段:
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")
The idea is to dynamically create one class method (like notify_fan_mail ) for each feed type. 我们的想法是为每种Feed类型动态创建一个类方法(如notify_fan_mail )。 It works almost great, the only problem is that the print statement always prints "notifying new_event", regardless of the method I call (same for notify_new_mail , notify_review , etc.). 它工作得非常好,唯一的问题是print语句总是打印“通知new_event”,无论我调用哪种方法(对于notify_new_mail , notify_review等都相同)。
I realize it's because it's using the last value assigned to type. 我意识到这是因为它使用了分配给键入的最后一个值。 My question is: how can I dynamically create methods that would use the correct value for type ? 我的问题是:如何动态创建使用正确值类型的方法 ?
Also, if I have this exact code in a Python file, is that the correct way to add methods to the Feed class, or is there a more elegant way? 另外,如果我在Python文件中有这个确切的代码,这是将方法添加到Feed类的正确方法,还是有更优雅的方法?
Use a closure to preserve the value of kind
: 使用闭包来保留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))
By the way, don't use type
as a variable name since it shadows the builtin of the same name. 顺便说一句,不要使用type
作为变量名,因为它会影响同名的内置。
A more elegant way to modify Feed
is to create a class decorator. 修改Feed
一种更优雅的方法是创建一个类装饰器。 This makes it clearer that you have code modifying the original definition of 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")
The bug is caused by the nature of closures in Python. 该错误是由Python中的闭包性质引起的。 The name type
in your notify functions is bound to type
in the enclosing scope. 通知函数中的名称type
必须在封闭范围中type
。 When you change type
's value it changes for all closures referring to it. 当你改变type
的值时,它会改变引用它的所有闭包。
One way to solve this is use a function factory: 解决此问题的一种方法是使用函数工厂:
def make_notify_function(type):
def notify(self, **kwargs):
print "notifying %s" % type
self.create(type, **kwargs)
return notify
The issue you're running into is that your notify
function isn't encapsulating the value type
, just it's name. 您遇到的问题是您的notify
功能没有封装值type
,只是它的名称。 So when your for loop goes onto the next tuple, the old one is lost. 因此,当你的for循环进入下一个元组时,旧元素会丢失。
You can fix this by making type
a default argument to the function: 你可以通过使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)
...
Note that I've changed the self
argument to cls
, which is probably more correct, since you're making it a class method. 请注意,我已将self
参数更改为cls
,这可能更正确,因为您将其设为类方法。
I think this is an appropriate way to go to add methods to a class at runtime. 我认为这是在运行时向类添加方法的合适方法。 I'm not sure if that's necessarily something that you need to be doing, but without more information about your task (for instance, what does do_create
do?) I don't see any other obvious improvements. 我不确定这是否是你需要做的事情,但没有关于你的任务的更多信息(例如, do_create
做了什么?)我没有看到任何其他明显的改进。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.