2012-06-14 19 views
6

Tôi có một số đối tượng thuộc nhiều loại khác nhau (tên hàm khác nhau, chữ ký khác nhau) và tôi vá chúng để có cách phổ biến để truy cập chúng từ các chức năng khác nhau. Tóm lại, có một điều phối viên đưa các đối tượng mà tôi muốn vá và tùy thuộc vào kiểu đối tượng mà nó gọi là trình vá khác nhau. Một Patcher sẽ bổ sung thêm phương pháp để các đối tượng:Thêm lớp cơ sở vào đối tượng hiện có trong python

def patcher_of_some_type(object): 

    def target(self, value): 
     # do something and call self methods 

    object.target = types.MethodType(target, object) 

    # many more of these 

Là chương trình phát triển phức tạp hơn thực hiện một wrapper xung quanh đối tượng (hoặc các lớp đối tượng) có vẻ là ý tưởng tốt hơn. Một số bản vá có chung mã hoặc tương quan với nhau. Nhưng tôi không kiểm soát việc tạo đối tượng, cũng không tạo ra lớp học. Tôi chỉ lấy được đồ vật. Và ngay cả khi tôi có thể làm điều đó, tôi chỉ muốn bọc (hoặc vá) một số vật thể, không phải tất cả.

Một giải pháp có thể là thêm lớp cơ sở vào đối tượng hiện có, nhưng tôi không chắc chắn về cách bảo trì và an toàn này. Có giải pháp nào khác không?

+0

Vì vậy, các lớp học không được xác định từ bạn? – dav1d

+0

Không, các lớp học được định nghĩa trong một thư viện mà tôi không kiểm soát. Tôi có thể tạo trình bao bọc từ tất cả các lớp, nhưng điều này sẽ chỉ làm việc cho các đối tượng mà tôi khởi tạo, không phải cho các đối tượng được thư viện khởi tạo. Nếu tôi vá thư viện, cuộc trò chuyện sẽ xảy ra: tất cả các đối tượng đều được vá và tôi chỉ muốn vá một số thứ đó. Cuối cùng, nếu tôi bao bọc một số đối tượng, tôi sẽ cần giữ cơ sở dữ liệu của riêng mình để chỉ chúng. Đó là lý do tại sao bây giờ tôi đang vá các đồ vật mà tôi muốn. – Hernan

+1

Tôi sẽ định nghĩa một lớp bao bọc để giữ các đối tượng. Sự cần thiết phải có các loại trình bao bọc khác nhau chia sẻ mã chung có thể được xử lý bằng cách có lớp trình bao bọc cơ sở và phát sinh các phiên bản chuyên biệt từ nó. Công cụ OOP cơ bản ... – martineau

Trả lời

9

động thay đổi kiểu của một đối tượng là an toàn hợp lý, như miễn là lớp cơ sở bổ sung tương thích (và bạn sẽ nhận được một ngoại lệ nếu nó không phải là). Cách đơn giản nhất để thêm một lớp cơ sở là với 3 đối số type constructor:

cls = object.__class__ 
object.__class__ = cls.__class__(cls.__name__ + "WithExtraBase", (cls, ExtraBase), {}) 
+1

sử dụng loại (và chỉ cần sử dụng 'type' thay vì' object .__ class __.__ class__', mà btw không thành công với các lớp kiểu cũ!) Chỉ cần thiết nếu bạn cần đặt tên lớp, nếu không bạn chỉ có thể làm 'lớp AnyNameHere (cls, ExtraBase): pass' dễ đọc hơn. – l4mpi

0

Để thêm một lớp khác lớp căn cứ, bạn có thể làm một cái gì đó như thế này:

def pacher_of_some_type(object): 

    #this class holds all the stuff you want to add to the object 
    class Patch(): 
     def target(self, value): 
      #do something 

    #add it to the class' base classes 
    object.__class__.__bases__ += (Patch,) 

Nhưng điều này sẽ ảnh hưởng đến tất cả các đối tượng của lớp này như bạn thực sự thay đổi lớp chính nó, mà không có vẻ là những gì bạn muốn ... nếu bạn chỉ muốn vá một Instance, bạn có thể phân lớp lớp ban đầu và thay đổi lớp trường hợp động:

class PatchedClass(object.__class__): 
    def target(self, value): 
     #do whatever 

object.__class__ = PatchedClass 

Xét về độ an toàn và khả năng quản lý, tôi muốn nói thêm một động lớp cơ sở là duy trì như tự động thêm một số dấu chấm câu ns và an toàn hơn vì bạn ít có khả năng vô tình ghi đè lên một số hàm bên trong của lớp gốc vì lớp cơ sở mới được thêm vào cuối tuple, có nghĩa là nó được sử dụng cuối cùng cho độ phân giải tên. Nếu bạn thực sự muốn ghi đè lên các phương thức, hãy thêm chúng vào đầu của các lớp cơ sở tuple (tất nhiên điều này không áp dụng cho cách tiếp cận phân lớp). Đừng hỏi tôi nếu động thay đổi lớp của một ví dụ là an toàn ở tất cả, nhưng trong một ví dụ tầm thường nó không có vẻ là một vấn đề:

class A(): 
    def p(self): print 1 

class B(): 
    def p(self): print 2 

a = A() 
a.p() #prints 1 
a.__class__ = B 
a.p() #prints 2 
2

Dựa trên một comment về câu trả lời khác, đây là một cách khác để vá các lớp cơ sở:

class Patched(instance.__class__, ExtraBase): 
    pass 

instance.__class__ = Patched 
+1

Điều này thực sự gọn gàng! Nó dường như thay đổi tên lớp để Patched, tuy nhiên. Bạn có thể vượt qua điều đó bằng cách thêm một dòng: 'Đã vá .__ name__ = instance .__ class __.__ name__'. – paulmelnikow