【问题标题】:__instancecheck__ - overwrite shows no effect - what am I doing wrong?__instancecheck__ - 覆盖没有效果 - 我做错了什么?
【发布时间】:2019-02-09 15:29:20
【问题描述】:

我试图让我的类显示为不同的对象,以规避我正在使用的包中的惰性类型检查。更具体地说,我试图让我的对象显示为另一个对象的实例(在我的情况下为tuple),而实际上它甚至不是它的派生。

为了实现这一点,我计划覆盖__isinstance__ 方法,根据docs,它应该完全符合我的要求。但是,我似乎不明白该怎么做,因为我的尝试没有成功。

这是一个SSCCE,应该isinstance在所有情况下都返回False,但不会。

class FalseInstance(type):
    def __instancecheck__(self, instance):
        return False

class Foo(metaclass=FalseInstance):
    pass

g = Foo()
isinstance(g, Foo)
> True

我做错了什么?

【问题讨论】:

  • 你的非元组类是什么样的?它是否实现了您的包所需的所有元组方法?对于您的问题,我能想到的唯一真正的解决方案是子类元组,但要覆盖它的所有方法。
  • @Dunes 谢谢,我调整了问题。我现在也明白为什么我提供的代码不起作用了。但是,如果我现在翻转示例并将其更改为 __isinstance__ 始终返回 True,它仍然无法按我预期的方式工作:isinstance(g, int) 仍然返回 False .这里有什么问题?
  • @Dunes 对于第二个问题 - tuples 是不可变的,因此使用起来有点麻烦,对吧?我还不知道包的工作需要元组的哪些方面,但我想我会在抛出异常时添加它们......

标签: python python-3.x metaclass magic-methods isinstance


【解决方案1】:

除了__metaclass__ 的问题和精确类型匹配的快速路径之外,__instancecheck__ 的工作方向与您尝试做的相反。一个类的__instancecheck__ 检查其他对象是否被视为该类的虚拟实例,而不是该类的实例是否被视为其他类的虚拟实例。

如果您希望您的对象在isinstance 检查中谎报其类型(您真的不应该),那么这样做的方法是谎报__class__,而不是实现__instancecheck__

class BadIdea(object):
    @property
    def __class__(self):
        return tuple

print(isinstance(BadIdea(), tuple)) # prints True

顺便说一句,如果您想获取对象的实际类型,请使用type 而不是检查__class__isinstance

【讨论】:

  • Eeeek。这确实很可怕。不知道是不是一个实现细节isinstance被这个忽悠了。
  • @jsbueno:我不确定。标准库依赖于它,尤其是在 unittest.mock 中,但 isinstance__class__ 的文档没有提到它。 C API docs 提到它,但 C API 是一个实现细节。
  • 这直接违背了 PEP 的描述,所以可能是一个错误。但是,是的,在调用 __instancecheck__ 之前,快速路径会首先检查直接类型。
  • 参见this bug report,目前似乎还没有就这是错误还是文档问题达成共识。
【解决方案2】:

如果您在FalseInstance.__instancecheck__ 中添加打印,您将看到它甚至没有被调用。但是,如果您调用isinstance('str', Foo),那么您会看到FalseInstance.__instancecheck__ 确实被调用了。

这是由于isinstance 的实现中的优化,如果type(obj) == given_class 立即返回True

int
PyObject_IsInstance(PyObject *inst, PyObject *cls)
{
    _Py_IDENTIFIER(__instancecheck__);
    PyObject *checker;

    /* Quick test for an exact match */
    if (Py_TYPE(inst) == (PyTypeObject *)cls)
        return 1;
    .
    .
    .
}

From Python's source code

【讨论】:

  • 真的吗?如果不完全改变这种行为,即使提供 isinstance 又有什么意义?
  • 看起来在 python 2.7 中它确实有效...:stackoverflow.com/a/13135792/8179099
  • @Nearoo: __instancecheck__ 主要是为了让更多的isinstance 支票通过,而不是更少。另外,没有__isinstance__
  • 但是,如果我现在翻转示例并将其更改为 __isinstance__ 始终返回 True,它仍然无法按我期望的方式工作: isinstance(g, int) 现在返回 False。这里有什么问题?
  • @Nearoo:没有__isinstance__只有__instancecheck__,它的工作方向与你想要做的相反。
猜你喜欢
  • 2019-07-26
  • 2012-05-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-15
  • 1970-01-01
相关资源
最近更新 更多