让'isinstance'与装饰器一起工作

3 投票
4 回答
1365 浏览
提问于 2025-04-17 02:50

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)

这意味着 FooDecorator 类的一个实例。因为 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 的一个 子类

附注:我对这一切的目的不是很清楚;可能使用元类会更好地实现你想做的事情。

撰写回答