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. 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.).
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?
The issue you're running into is that your
notify
function isn't encapsulating the valuetype
, just it's name. So when your for loop goes onto the next tuple, the old one is lost.You can fix this by making
type
a default argument to the function:Note that I've changed the
self
argument tocls
, which is probably more correct, since you're making it a class method.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.The bug is caused by the nature of closures in Python. The name
type
in your notify functions is bound totype
in the enclosing scope. When you changetype
's value it changes for all closures referring to it.One way to solve this is use a function factory:
Use a closure to preserve the value of
kind
:By the way, don't use
type
as a variable name since it shadows the builtin of the same name.A more elegant way to modify
Feed
is to create a class decorator. This makes it clearer that you have code modifying the original definition ofFeed
.