【发布时间】:2009-02-23 22:17:28
【问题描述】:
在 Python 中,我看到了使用保持或包装来扩展对象或类的功能而不是继承的建议。特别是,我认为 Alex Martelli 在他的 Python Design Patterns 演讲中谈到了这一点。我已经在库中看到这种模式用于依赖注入,例如pycontainer。
我遇到的一个问题是,当我必须与使用
isinstance anti-pattern,此模式失败,因为持有/包装对象未通过 isinstance 测试。如何设置保持/包装对象以绕过不必要的类型检查?这可以通用吗?从某种意义上说,我需要类似于保留签名的函数装饰器的类实例(例如,simple_decorator 或 Michele Simionato 的 decorator)。
限定条件:我并不是说所有isinstance 的使用都是不恰当的;几个答案对此提出了很好的观点。也就是说,应该认识到isinstance 的使用对对象交互造成了重大限制——它迫使继承成为多态性的来源,而不是行为。
对于究竟是如何/为什么会出现问题似乎有些困惑,所以让我提供一个简单的例子(大致摘自pycontainer)。假设我们有一个类 Foo,以及一个 FooFactory。为了这个例子,假设我们希望能够实例化记录每个函数调用的 Foo 对象,或者不考虑 AOP。此外,我们希望在不以任何方式修改 Foo 类/源的情况下做到这一点(例如,我们实际上可能正在实现一个通用工厂,它可以动态地将日志记录功能添加到任何类实例)。对此的第一次尝试可能是:
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!
您可能希望完全以这种方式做事(使用装饰器等),但请记住:
- 我们不想接触 Foo 类。假设我们正在编写可供我们还不了解的客户使用的框架代码。
- 重点是返回一个本质上是 Foo 的对象,但具有附加功能。对于任何其他期望 Foo 的客户端代码,它应该尽可能地显示为 Foo。因此希望在
isinstance周围工作。 - 是的,我知道我不需要工厂类(在这里先发制人地为自己辩护)。
【问题讨论】:
-
面对实例,我无助地愤怒地握着我的拳头。你不能用 isinstance 修复代码吗? stackoverflow.com/questions/423823/…
-
无助的愤怒 :) 在某些情况下,我可以访问 isinstance。但另一个用例是想要编写与其他人相处得很好的“框架”代码(例如,上面的 pycontainer)。