2010-05-03 23 views
7

Tôi cần tìm một cách thanh lịch để thực hiện 2 loại MixIn.Những cách thanh lịch để làm MixIns bằng Python là gì?

Đầu tiên:

class A(object): 
    def method1(self): 
     do_something() 

Bây giờ, một MixInClass nên method1 làm điều này: do_other() ->A.method1() ->do_smth_else() - ví dụ: về cơ bản "quấn" chức năng cũ. Tôi khá chắc chắn phải tồn tại một giải pháp tốt cho việc này.

Thứ hai:

class B(object): 
    def method1(self): 
     do_something() 
     do_more() 

Trong trường hợp này, tôi muốn MixInClass2 để có thể tiêm tự giữa do_something()do_more(), tức là .: do_something() ->MixIn.method1 ->do_more(). Tôi hiểu rằng có lẽ điều này sẽ yêu cầu sửa đổi class B - đó là ok, chỉ cần tìm cách đơn giản nhất để đạt được điều này.

Đây là những vấn đề khá tầm thường và tôi thực sự đã giải quyết chúng, nhưng giải pháp của tôi bị nhiễm độc.

Bằng cách sử dụng self._old_method1 = self.method1(); self.method1() = self._new_method1(); và viết _new_method1() gọi tới _old_method1().

Sự cố: nhiều MixIns sẽ đổi tên thành _old_method1 và nó không phù hợp.

Kết hợp thứ haiTrong một giải pháp được giải quyết bằng cách tạo phương pháp giả call_mixin(self): pass và tiêm giữa các cuộc gọi và xác định self.call_mixin(). Một lần nữa không thích hợp và sẽ phá vỡ nhiều MixIns ..

Bất kỳ ý tưởng nào?


Nhờ Boldewyn, tôi đã tìm thấy giải pháp thanh lịch để đầu tiên (tôi đã quên bạn có thể tạo trang trí on-the-fly, mà không sửa đổi mã ban đầu):

class MixIn_for_1(object): 
    def __init__(self): 
     self.method1 = self.wrap1(self.method1) 
     super(MixIn_for_1, self).__init__() 

    def wrap1(self, old): 
     def method1(): 
      print "do_other()" 
      old() 
      print "do_smth_else()" 
     return method1 

Vẫn tìm kiếm cho ý tưởng cho ý tưởng thứ hai (ý tưởng này sẽ không phù hợp, vì tôi cần phải tiêm bên trong phương pháp cũ, không phải bên ngoài, như trong trường hợp này).


Giải pháp thứ hai dưới đây, thay thế "pass_func" bằng lambda:0.

+0

Đối với câu hỏi thứ 2 của bạn, mixin.method1 có cần tham số không và nếu có, các tham số này có phụ thuộc vào biến/giá trị trả về trong do_something() không? Và cuối cùng, do_more() có cần kết quả mixin của bạn không? – KillianDS

+0

Không, do_more() không cần kết quả. Các đối số? Tôi có lẽ chỉ cần "tự", mà nên được thông qua anyway. –

Trả lời

3

Đây là một cách khác để thực hiện MixInClass1, MixinClass2:

trang trí rất hữu ích khi bạn cần phải quấn nhiều chức năng. Kể từ MixinClass1 cần phải quấn chỉ có một chức năng, tôi nghĩ rằng đó là rõ ràng hơn để khỉ vá:

Sử dụng dấu gạch dưới đôi cho __old_method1__method1 đóng một vai trò hữu ích trong MixInClass1.Bởi vì quy ước mang tên của Python, sử dụng dấu gạch dưới kép bản địa hoá các thuộc tính này thành MixinClass1 và cho phép bạn sử dụng cùng tên thuộc tính cho các lớp kết hợp khác mà không gây ra các va chạm tên không mong muốn.

class MixInClass1(object): 
    def __init__(self): 
     self.__old_method1,self.method1=self.method1,self.__method1 
     super(MixInClass1, self).__init__()   
    def __method1(self): 
     print "pre1()" 
     self.__old_method1() 
     print "post1()" 

class MixInClass2(object): 
    def __init__(self): 
     super(MixInClass2, self).__init__()   
    def method1_hook(self): 
     print('MixIn method1') 

class Foo(MixInClass2,MixInClass1): 
    def method1(self): 
     print "do_something()" 
     getattr(self,'method1_hook',lambda *args,**kw: None)() 
     print "do_more()" 

foo=Foo() 
foo.method1() 
+0

Giải pháp đầu tiên chính xác giống như tôi đã mô tả trong câu hỏi. Thứ hai là thông minh, giả sử 'Foo' là' B' và pass_func là 'lambda: 0'. –

+0

@Slava: Nếu bạn sử dụng 'pass_func', thì bạn sẽ không phải thay đổi' Foo' nếu sau này bạn quyết định chuyển đối số sang 'method1_hook'. Nếu bạn sử dụng 'lambda: 0' thì bạn sẽ phải thay đổi nó thành một cái gì đó như' lambda x, y, z: 0'. – unutbu

+0

1. 'pass_func' là gì? 2. Chúng ta không thể làm 'lambda * args, ** kwargs: 0'? –

5

Tôi nghĩ rằng, có thể được xử lý theo cách khá Pythonic sử dụng decorators. (PEP 318, quá)

+0

Cảm ơn bạn đã giải quyết được vấn đề đầu tiên của tôi. Tôi đã làm trong thực tế nghĩ về trang trí, nhưng tôi đã quên rằng tôi có thể xây dựng trang trí như deco (phương pháp), thay vì @ deco. Sau này yêu cầu sửa đổi mã gốc. –

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