Trong Python, tôi đã thấy đề xuất sử dụng giữ hoặc gói để mở rộng chức năng của đối tượng hoặc lớp, thay vì thừa kế. Đặc biệt, tôi nghĩ rằng Alex Martelli đã nói về điều này trong bài nói chuyện Python Design Patterns của mình. Tôi đã thấy mẫu này được sử dụng trong các thư viện để tiêm phụ thuộc, như pycontainer.Đóng gói các đối tượng để mở rộng/thêm chức năng trong khi làm việc xung quanh isinstance
Một vấn đề mà tôi đã gặp phải là khi tôi phải giao diện với mã sử dụng isinstance anti-pattern, mẫu này không thành công vì đối tượng giữ/gói không thực hiện kiểm tra isinstance
. Làm thế nào tôi có thể thiết lập đối tượng giữ/gói để kiểm tra loại không cần thiết? Điều này có thể được thực hiện chung? Theo một nghĩa nào đó, tôi cần một thứ gì đó cho các cá thể lớp tương tự với các trang trí chức năng bảo quản chữ ký (ví dụ: simple_decorator hoặc decorator của Michele Simionato).
Chứng chỉ: Tôi không khẳng định rằng tất cả việc sử dụng isinstance
là không phù hợp; một số câu trả lời làm cho các điểm tốt về điều này. Điều đó nói rằng, cần phải nhận ra rằng việc sử dụng isinstance
đặt ra những hạn chế đáng kể về tương tác đối tượng --- nó buộc thừa kế là nguồn đa hình, thay vì hành vi.
Dường như có sự nhầm lẫn về chính xác/tại sao đây là vấn đề, vì vậy hãy để tôi cung cấp một ví dụ đơn giản (được nâng cấp rộng rãi từ pycontainer). Giả sử chúng ta có một lớp Foo, cũng như một FooFactory. Vì lợi ích của ví dụ này, giả sử chúng ta muốn có thể khởi tạo các đối tượng Foo để ghi lại mọi cuộc gọi hàm, hoặc không nghĩ là AOP. Hơn nữa, chúng tôi muốn thực hiện điều này mà không sửa đổi lớp/nguồn Foo theo bất kỳ cách nào (ví dụ: chúng tôi có thể triển khai một nhà máy chung có thể thêm khả năng ghi nhật ký cho bất kỳ cá thể lớp nào đang chạy). Một đâm đầu tiên tại này có thể là:
class Foo(object):
def bar():
print 'We\'re out of Red Leicester.'
class LogWrapped(object):
def __init__(self, wrapped):
self.wrapped = wrapped
def __getattr__(self, name):
attr = getattr(self.wrapped, name)
if not callable(attr):
return attr
else:
def fun(*args, **kwargs):
print 'Calling ', name
attr(*args, **kwargs)
print 'Called ', name
return fun
class FooFactory(object):
def get_foo(with_logging = False):
if not with_logging:
return Foo()
else:
return LogWrapped(Foo())
foo_fact = FooFactory()
my_foo = foo_fact.get_foo(True)
isinstance(my_foo, Foo) # False!
Lý do có nhiều lý do tại sao bạn có thể muốn làm những việc chính xác theo cách này (sử dụng trang trí thay vào đó, vv), nhưng cần lưu ý:
- Chúng tôi don không muốn chạm vào lớp Foo. Giả sử chúng tôi đang viết mã khung công tác có thể được sử dụng bởi khách hàng mà chúng tôi chưa biết.
- Vấn đề là trả về một đối tượng chủ yếu là Foo, nhưng với chức năng bổ sung. Nó dường như là một Foo --- nhiều nhất có thể --- với bất kỳ mã khách hàng nào khác mong đợi một Foo. Do đó mong muốn làm việc xung quanh
isinstance
. - Có, tôi biết rằng tôi không cần lớp nhà máy (bảo vệ trước bản thân mình ở đây).
Lắc nắm tay trong cơn giận dữ bất lực lúc đang cố gắng. Bạn không thể sửa mã với isinstance? http://stackoverflow.com/questions/423823/whats-your-favorite-programmer-ignorance-pet-peeve/423857#423857 –
Fury bất lực :) Trong một số trường hợp, tôi có quyền truy cập vào isinstance. Tuy nhiên, một trường hợp sử dụng khác sẽ muốn viết mã "khung" (ví dụ: người bảo vệ ở trên) có vai trò tốt đẹp với những người khác. – zweiterlinde