2012-01-04 31 views
7

Tôi có một lớp học trong dự án chính mà tôi không muốn thay đổi.Làm thế nào để gọi phương thức gốc khi nó được vá khỉ?

class A(): 
    def __init__(self, firstname, lastname): 
     self.firstname = firstname 
     self.lastname = lastname 

    def name(self): 
     # this method could be much more complex 
     return self.lastname.upper() 

Tôi đang cố gắng xây dựng một mechansim plugin. Cho đến nay rất tốt, tôi có một điểm mở rộng như thế này:

if __name__ == '__main__': 
    ''' The main project has an extension point that allows me to do''' 
    # for each class extension such as AExtended: 
    A.name = AExtended.name 
    ''' After the extensions are loaded, some behaviours may be changed''' 
    a = A("John", "Doe") 
    print(a.name()) 

Một plugin có thể được viết như thế này:

class AExtended(A): 
    ''' This is an extension I provide through a plugin mechanism 
    ''' 
    def name(self): 
     return self.firstname + ' ' + self.lastname.upper() 

này tất cả các hoạt động rất tốt. Bây giờ tôi nhận được "John DOE".

Vấn đề của tôi là phương thức gốc name() có thể khá phức tạp. Nói cách khác, tôi không thể gọi số self.lastname.upper() trong số AExtended. Tôi muốn gọi phương thức "siêu", không tồn tại nữa, bởi vì nó đã bị ghi đè.

Làm thế nào tôi có thể thay đổi mã của tôi, để đạt được một cái gì đó như thế này:

class AExtended(A): 
    def name(self): 
     # I'm looking for a way to call the base implementation that was in A.name() 
     return self.firstname + ' ' + parent.name() 

Nhờ sự giúp đỡ của bạn!

Chỉnh sửa: Một số giải thích về những gì tôi cố gắng làm.

  • Tôi muốn plugin cập nhật hành vi của A. Tôi không thể đủ khả năng để thay đổi tiêu dùng hiện có của A
  • Có rất nhiều lớp học như A mà có thể được thay đổi, tôi muốn bổ sung để có thể kiểm soát đầy đủ và trách nhiệm
  • Đúng AExtended không nhất thiết phải kế thừa từ A, nhưng đó là cách dễ dàng để truy cập self.firstname. Tôi không gặp vấn đề gì sau một mẫu thiết kế khác nếu nó có thể giúp.

Tôi có một cách giải quyết, nhưng nó không phải là rất tao nhã và khó có thể khái quát

class AExtended(A): 
    def name(self): 
     # I'm looking for a way to call the base implementation that was in A.name() 
     return self.firstname + ' ' + self.parentname() 
#in main 
A.parentname = A.name 
A.name = AExtended.name 
+0

Vâng, tôi đọc cách [gọi phương thức lớp cha] (http://stackoverflow.com/questions/805066/how-to-call-a-parent-classs-method-from- child-class-in-python) nhưng trong 'AExtended.name',' self' là 'A' vì ghi đè – rds

+0

Tôi có thể có nhiều lcuky hơn với [ghi đè một phương thức riêng] (http://stackoverflow.com/ câu hỏi/2484215/how-do-i-ghi đè-một-cha-class-chức năng-in-python) nhưng tôi không thể thay đổi tên phương thức A. – rds

+0

Với sự giải thích của bạn, tôi sẽ đề nghị bạn thay đổi tiêu đề câu hỏi. Một tiêu đề chính xác hơn có thể là "Cách gọi phương thức ban đầu khi nó được vá khỉ?" – Weeble

Trả lời

3

Dưới đây là ví dụ đầy đủ về những gì tôi đã gợi ý. Cảm thấy tự do để hét lên với tôi và có tôi kết hợp câu trả lời của tôi, hoặc downvote một hoặc bất cứ điều gì, chỉ cần dễ dàng hơn để cung cấp một thay thế như một câu trả lời mới. Tôi sẽ để cho đoạn mã thực hiện cuộc nói chuyện thay vì giải thích nó.:)

## Some shared class that is used all over the place and needs to be patched. 

class A(object): 
    def __init__(self): 
     self.firstname = 'Bob' 

    # Print my first name. 
    def name(self): 
     return self.firstname 

    # Use this to allow patching arbitrary methods... 
    @classmethod 
    def patch(cls, func_name): 
     def patch_by_name(new_func): 
      old_func = getattr(cls, func_name) 
      def patched_func(self): 
       return new_func(self, old_func) 
      setattr(cls, func_name, patched_func) 
     return patch_by_name 

## Some other area of the code where you want to throw in a patch 

class PatchedA(A): # doesn't need to subclass, but comes in handy sometimes 
    @A.patch('name') 
    def name(self, orig_func): 
     return 'I am ' + orig_func(self) + 'McWizwaz' 

print 'Who are you, A class?' 
print A().name() # prints 'I am Bob McWizwaz' 
+0

(Và tất nhiên, bạn được tự do làm cho nó không phải là một classmethod và chỉ là một chức năng độc lập và gọi nó là '@patch (A, 'name')' thay vào đó, do đó làm cho nó có thể sử dụng cho bất kỳ lớp nào ở bất cứ đâu.) – Spencer

+0

Wouldn Sẽ dễ dàng hơn khi đặt trang trí '@ patch' đó bên ngoài' A' và cũng áp dụng nó cho 'def name' _outside_ vá bất kỳ lớp con nào? Chính xác thì mục đích của phân lớp 'PatchedA' là gì, khi bạn thay thế phương thức' A' gốc? –

0

Một lựa chọn có thể không luôn luôn là tốt nhất trong một ngôn ngữ như Python là sử dụng @override trang trí từ this non-standard package. Nhưng đây là một lựa chọn khả thi chỉ khi hai hàm của bạn hoạt động trên các loại khác nhau hoặc số lượng đối số khác nhau. Ngoài ra, bạn không thể làm gì ngoài việc đổi tên chức năng của mình.

+0

Thú vị gói, nhưng tôi không thấy làm thế nào nó có thể giúp tôi: -/ – rds

1
class ABase(object): 
    def name(self): 
     pass 

class A(object): 
    pass 

class AExtension(ABase): 
    def name(self): 
     return ABase.name(self) 

A.name = AExtension.name 
+0

Tôi không chắc tôi hiểu những gì lớp nên được sử dụng. Tôi cần phải sao chép tất cả các phương pháp từ 'ABase' sang' A' ('A.name = ABase.name' vv) và sau đó tải phần mở rộng (' A.name = AExtension.name')? – rds

+0

'A' đã có tất cả các phương thức' ABase' vì 'A' mở rộng' ABase'. Nhưng tổng thể ý tưởng của việc gán lại các phương thức cho lớp khác có vẻ đáng ngờ. Bạn có thể tìm thấy một số ý tưởng làm thế nào để chính xác một kiến ​​trúc plugin tối thiểu trong chủ đề này: http://stackoverflow.com/questions/932069/building-a-minimal-plugin-architecture-in-python – Ski

+0

@Skirmantas, nó không phải là không phổ biến để trang trí các chức năng như anh ấy muốn làm. Các phiên bản mới hơn của ngôn ngữ thậm chí còn hỗ trợ cú pháp cú pháp để làm cho ví dụ của tôi dễ viết hơn. – Spencer

12

Đây là thứ mà chúng tôi gọi là mẫu 'trang trí'. Thay thế việc gán lại tên ban đầu để nó thay thế cho hàm ban đầu. Sau đó nó trả về một hàm mới.

def name_decorator(method): 
    def decorate_name(self=None): 
     return stuff + method(self) 
    return decorate_name 
A.name = name_decorator(A.name) 

Sau đó, gọi A.name sẽ gọi decorate_name với self như trường hợp hiện tại và method sẽ có sẵn cho nó mà chỉ vào chức năng tại thời điểm tái bổ nhiệm.

+0

+1 bởi vì trang trí python đang làm chính xác những gì tôi muốn. Tôi sẽ chấp nhận câu trả lời này khi tôi có thể kiểm tra nó có thể được tổng quát hóa. – rds

+0

Tôi sử dụng Python 3. Đường cú pháp nào có thể giúp? – rds

+0

Xem: http://docs.python.org/reference/compound%5Fstmts.html#function - thay vì thực hiện ghi đè rõ ràng, bạn có thể đặt '@ my_decorator' ngay trước bất kỳ chức năng nào mà bạn muốn trang trí. – Spencer

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