2013-03-14 50 views
5

Tôi có đoạn như sau:động thêm các phương pháp lớp học để một lớp

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

Ý tưởng là để tự động tạo ra một phương thức lớp (như notify_fan_mail) đối với từng loại thức ăn. Nó hoạt động gần như tuyệt vời, vấn đề duy nhất là báo cáo in luôn in "thông báo new_event", bất kể phương thức tôi gọi (giống với notify_new_mail, notify_review, v.v.).

Tôi nhận ra đó là vì nó đang sử dụng giá trị cuối cùng được gán cho loại. Câu hỏi của tôi là: làm cách nào tôi có thể tự động tạo các phương thức có thể sử dụng giá trị chính xác cho loại?

Ngoài ra, nếu tôi có mã chính xác này trong tệp Python, đó có phải là cách chính xác để thêm phương thức vào lớp Nguồn cấp dữ liệu hoặc có cách thanh lịch hơn không?

Trả lời

5

Sử dụng một đóng cửa để bảo tồn giá trị của 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)) 

Bằng cách này, không sử dụng type như một tên biến vì nó bóng tối các BUILTIN cùng tên.


Cách thanh lịch hơn để sửa đổi Feed là tạo trình trang trí lớp học. Điều này làm cho nó rõ ràng hơn rằng bạn có mã sửa đổi định nghĩa ban đầu của 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

Cảm ơn bạn! Các dòng 'notify = make_notify (typ)' và 'notify .__ name__ =" notify_% s "% typ' nên sử dụng' type' (thay vì 'typ'), đúng không? – kolrie

+0

Rất tiếc, 'self.create (type, ...)' phải là 'self.create (typ, ...)'.Ở khắp mọi nơi bạn đã viết 'loại', tôi đề nghị sử dụng một cái gì đó khác nhau, có thể' loại' thay thế - để hoàn toàn phân biệt nó với nội trang Python. – unutbu

+0

Yêu thích khái niệm trang trí lớp học! – kolrie

1

Lỗi do bản chất của các bao đóng bằng Python. Tên type trong các chức năng thông báo của bạn bị ràng buộc là type trong phạm vi kèm theo. Khi bạn thay đổi giá trị của type, nó thay đổi cho tất cả các bao đóng đề cập đến nó.

Một cách để giải quyết việc này là sử dụng một nhà máy chức năng:

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

Vấn đề bạn đang chạy vào đó là chức năng notify của bạn không được đóng gói giá trị type, chỉ cần tên của nó. Vì vậy, khi vòng lặp for của bạn đi vào tuple tiếp theo, cái cũ bị mất.

Bạn có thể khắc phục điều này bằng cách làm cho type một đối số mặc định cho chức năng:

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) 

    ... 

Lưu ý rằng tôi đã thay đổi lập luận self để cls, mà có lẽ đúng hơn, vì bạn đang làm cho nó một phương thức lớp.

Tôi nghĩ đây là một cách thích hợp để đi thêm các phương thức vào một lớp khi chạy. Tôi không chắc liệu đó có phải là điều bạn cần phải làm, nhưng không có thêm thông tin về nhiệm vụ của bạn (ví dụ: do_create làm gì?) Tôi không thấy bất kỳ cải tiến rõ ràng nào khác.

+0

Tôi nghĩ rằng điều này không thành công nếu hàm được gọi với nhiều hơn một đối số vị trí hoặc một đối số được đặt tên được gọi là 'type'. –

Các vấn đề liên quan