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

时间:2011-09-22 12:49:38

标签: python decorator 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行为几乎就像mixin一样,除了混音在这里不合适。有什么方法可以让上面的代码工作吗?我还应该注意isinstance行也会出现以下错误:

  

isinstance(f,Foo)
  TypeError:isinstance()arg 2必须是类型或类型元组

4 个答案:

答案 0 :(得分:5)

以下内容如何:

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()调用deco方法,然后转发到原始方法。

基本想法是确保装饰的Foo仍然是一个类,并确保装饰的Foo是原始的{em>子类 { {1}}。

P.S。这一切的目的对我来说并不完全清楚;可能是元类是实现你想要做的事情的更好方法。

答案 1 :(得分:1)

问题是你的例子中的Foo不是一个类。

此代码:

@Decorator
class Foo:
    pass

相当于:

class Foo:
    pass
Foo = Decorator(Foo)

这意味着Foo是类Decorator的实例。由于Foo不是clas或type,isinstance会抱怨。

答案 2 :(得分: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

答案 3 :(得分:-1)

如果Foo在没有调用Foo的情况下返回,则无法获取该对象的类型。

isinstance抱怨它的第二个参数,因为它是一个实例 - 在你的Decorated案例中。虽然你认为Foo就像一个类,但实际上它只是一个可调用的对象而且它不是一个类。

也许下一个会帮助您重新思考/解决您的问题:

>>> isinstance(f, Foo._decorated)
True