【问题标题】:Making 'isinstance' work with decorators让“isinstance”与装饰器一起工作
【发布时间】:2011-11-22 19:39:05
【问题描述】:

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 必须是类型或类型的元组

【问题讨论】:

  • 你到底想用这个实现什么?
  • 你还没有告诉我们你的装饰器是做什么的,所以很难知道什么会起作用。考虑使用装饰器更改类本身的属性,然后返回该类,或者使用元类而不是装饰器重写该类。

标签: python decorator isinstance


【解决方案1】:

以下内容如何:

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子类

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

【讨论】:

    【解决方案2】:

    问题是您的示例中的 Foo 不是一个类。

    这段代码:

    @Decorator
    class Foo:
        pass
    

    相当于:

    class Foo:
        pass
    Foo = Decorator(Foo)
    

    这意味着Foo 是类Decorator 的一个实例。因为Foo 不是类或类型,所以isinstance 会抱怨。

    【讨论】:

    • 这只是针对他的“还应该注意”,并没有告诉他如何使 isinstance 工作。
    【解决方案3】:

    如果不调用Foo,您将无法获取Foo 返回的对象的类型。

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

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

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

    【讨论】:

      【解决方案4】:

      当装饰一个类时,装饰的返回值也是类型通常是有用的或可取的;实现这一点最明显的方法是让装饰器构造并直接返回一个新类。

      该功能已由元类处理;事实上,元类比装饰器更强大一点,因为你可以在装饰类构建之前描述新类。

      另一种选择是返回传入的相同对象;但有一些变化。这对装饰器来说是一个更好的用途,因为它在嵌套装饰器时效果很好。由于您正在修改使用 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
      

      【讨论】:

        猜你喜欢
        • 2014-12-28
        • 1970-01-01
        • 2019-03-09
        • 2016-03-06
        • 2018-01-20
        • 2017-08-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多