让'isinstance'与装饰器一起工作
Python中的isinstance
函数是怎么运作的?有没有什么办法可以改变它的结果,比如在类里面定义一个特殊的函数之类的?这是我想要实现的情况:
class Decorator:
def __init__(self, decorated):
self._decorated = decorated
def __call__(self):
return self._decorated()
@Decorator
class Foo:
pass
f = Foo()
# How can I make this be true?
isinstance(f, Foo)
Decorator
的作用几乎像是一个混合类,但在这里用混合类不太合适。我有没有办法让上面的代码正常工作?我还想提一下,isinstance
这一行也出现了以下错误:
isinstance(f, Foo)
TypeError: isinstance() arg 2 must be a type or tuple of types
4 个回答
0
在给一个类添加装饰的时候,通常我们希望装饰后的返回值也能保持类型。这种情况下,最简单的方法就是让装饰器直接构建并返回一个新的类。
不过,这种功能其实已经被元类处理了。实际上,元类比装饰器更强大,因为你可以在装饰类还没被创建之前就描述新类的样子。
还有一种选择是返回传入的同一个对象,但做一些修改。这样使用装饰器会更合适,因为它在嵌套装饰器时效果很好。由于你是在修改使用 Foo()
时的行为,所以你可能也想修改 Foo 的 __init__
方法,可能看起来像这样:
>>> def Decorator(cls):
... assert isinstance(cls, type)
... try:
... old_init = cls.__init__.im_func
... except AttributeError:
... def old_init(self): pass
... def new_init(self):
... # do some clever stuff:
... old_init(self)
... cls.__init__ = new_init
... return cls
...
>>> @Decorator
... class Foo(object):
... def __init__(self): pass
...
>>> @Decorator
... class Bar(object):
... pass
...
>>> f = Foo()
>>> isinstance(f, Foo)
True
1
问题在于你例子中的 Foo
其实不是一个类。
这段代码:
@Decorator
class Foo:
pass
相当于:
class Foo:
pass
Foo = Decorator(Foo)
这意味着 Foo
是 Decorator
类的一个实例。因为 Foo
不是一个类或者类型,所以 isinstance
会报错。
6
下面的内容怎么样:
def Decorator(decorated):
class Dec(decorated):
def __call__(self):
print 'in decorated __call__'
return decorated.__call__(self)
return Dec
@Decorator
class Foo(object):
def __call__(self):
print 'in original __call__'
f = Foo()
# How can I make this be true?
print isinstance(f, Foo)
根据上面的代码:
isinstance(f, Foo)
这个检查是有效的;f()
会调用被装饰的方法,然后再转发到原始的方法。
基本的想法是确保被装饰的 Foo
仍然是一个类,并且确保被装饰的 Foo
是原始 Foo
的一个 子类。
附注:我对这一切的目的不是很清楚;可能使用元类会更好地实现你想做的事情。