2009-10-14 24 views
6

Tôi cần viết một lớp cho phép một lớp con thiết lập một thuộc tính với tên của một hàm. Hàm đó sau đó phải được gọi từ các thể hiện của lớp.Viết một lớp chấp nhận gọi lại bằng Python?

Ví dụ, tôi nói rằng tôi cần phải viết một lớp trái cây, nơi phân lớp có thể vượt qua trong một thông điệp chào mừng. Lớp Fruit phải trưng ra một thuộc tính print_callback có thể được thiết lập.

class Fruit(object): 
    print_callback = None 

    def __init__(self, *args, **kwargs): 
     super(Fruit, self).__init__(*args, **kwargs) 
     self.print_callback("Message from Fruit: ") 

tôi cần để lộ một API được có thể được tiêu thụ bởi mã này (phải rõ ràng, mã này không thể thay đổi, nói rằng nó là mã bên thứ 3):

def apple_print(f): 
    print "%sI am an Apple!" % f 

class Apple(Fruit): 
    print_callback = apple_print 

Nếu tôi chạy:

mac = Apple() 

tôi muốn nhận được:

Message from Fruit: I am an Apple!

Thay vào đó tôi nhận được:

TypeError: apple_print() takes exactly 1 argument (2 given)

Tôi nghĩ rằng điều này là do tự được chuyển vào làm đối số đầu tiên.

Vậy làm cách nào để viết lớp Fruit? Cảm ơn!

+1

Tôi đánh giá cao những vấn đề bạn đã có với cách tiếp cận này và những giải pháp tuyệt vời dưới đây. Tuy nhiên điều này trông giống như bạn nên định nghĩa 'print_callback' như một phương thức (có thể chỉ được gọi là' print_me', và nhận các lớp con để ghi đè lên nó.Điều đó sẽ đơn giản và nhiệt tình. Đối với mọi loại phương thức 'print_me' khác nhau, hãy tạo một lớp con khác. –

+0

Động lực của tôi cho việc đặt ra câu hỏi là tôi muốn phân lớp google.appengine.ext.db.djangoforms của Google App Engine (ví dụ như DF2) để cho phép ghi đè thuộc tính formfield_callback theo cách tương tự như ModelForm của Django (django.forms.ModelForm). Ý tưởng là 'lớp f (DF2)' sau đó có thể được sử dụng bởi Django tiêu chuẩn đơn giản bằng cách thay đổi lớp cha thành django.forms.ModelForm. Tôi không biết tại sao các tác giả Django định nghĩa formfield_callback là một thuộc tính. –

Trả lời

7

Python giả định rằng mọi hàm được ràng buộc trong phạm vi lớp là các phương thức. Nếu bạn muốn đối xử với họ như các chức năng, bạn phải đào xung quanh trong các thuộc tính của họ để lấy đối tượng chức năng ban đầu:

def __init__(self, *args, **kwargs): 
    super(Fruit, self).__init__(*args, **kwargs) 

    # The attribute name was changed in Python 3; pick whichever line matches 
    # your Python version. 
    callback = self.print_callback.im_func # Python 2 
    callback = self.print_callback.__func__ # Python 3 

    callback("Message from Fruit: ") 
+0

Hoặc tốt hơn, chỉ cần thêm đối số 'tự' vào hàm để nó có thể hoạt động như một phương thức bình thường. –

+0

Theo câu hỏi, định nghĩa gọi lại không thể thay đổi. –

+1

BTW, cũng đáng chú ý là thuộc tính 'im_func' của các phương thức đã được đổi tên thành' __func__' trong Python 3.x. –

2

Cập nhật: kết hợp abourget's suggestion sử dụng staticmethod:

Hãy thử điều này:

def __init__(self, *args, **kwargs): 
    super(Fruit, self).__init__(*args, **kwargs) 

    # Wrap function back into a proper static method 
    self.print_callback = staticmethod(self.print_callback) 

    # And now you can do: 
    self.print_callback("Message from Fruit: ") 
3

Bạn có thể sử dụng trực tiếp:

class Apple(Fruit): 
    print_callback = staticmethod(apple_print) 

hay:

class Apple(Fruit): 
    print_callback = classmethod(apple_print) 

Trong trường hợp đầu tiên, bạn sẽ nhận được chỉ có một tham số (bản gốc). Trong lần thứ hai, bạn sẽ nhận được hai tham số trong đó lớp đầu tiên sẽ là lớp mà nó được gọi.

Hy vọng điều này sẽ hữu ích và ngắn hơn và ít phức tạp hơn.

+1

"phải rõ ràng, mã này không thể thay đổi, nói rằng đó là mã của bên thứ 3" –

0

Ngoài ra còn có một giải pháp dirtyer bit với metaclasses:

def apple_print(f): 
    print "Apple " + f 

class FruitMeta(type): 
    def __new__(cls, name, bases, dct): 
     func = dct["print_callback"] 
     dct["print_callback"]=lambda x,f,func=func: func(f) 
     return type.__new__(cls,name,bases,dct) 

class Fruit(object): 
    __metaclass__ = FruitMeta 
    print_callback = None 
    def __init__(self): 
     super(Fruit,self).__init__() 
     self.print_callback("Msg ") 

class Apple(Fruit): 
    print_callback = apple_print 

mac = Apple()here 

Nó thao túng các lớp trước khi tạo ra nó!

1

tôi đang tìm kiếm một cái gì đó như thế này khi tôi tìm thấy câu hỏi này:

class Something: 
    def my_callback(self, arg_a): 
     print arg_a 

class SomethingElse: 
    def __init__(self, callback): 
     self.callback = callback 

something = Something() 
something_else = SomethingElse(something.my_callback) 
something_else.callback("It works...") 
Các vấn đề liên quan