gpt4 book ai didi

python - 为什么这个测试失败了?

转载 作者:太空宇宙 更新时间:2023-11-03 15:31:05 34 4
gpt4 key购买 nike

添加新的单元测试后,我开始在新测试后的不相关测试运行中遇到失败。我不明白为什么。

我已将案例简化为以下代码。我仍然不明白发生了什么。我很惊讶注释掉看似无关的代码行会影响结果:删除对 isinstance 的调用在 Block.__init__更改 isinstance(blk, AddonDefault) 的结果在 test_addons .

import abc

class Addon:
pass

class AddonDefault(Addon, metaclass=abc.ABCMeta):
pass

class Block:
def __init__(self):
isinstance(self, CBlock)

class CBlock(Block, metaclass=abc.ABCMeta):
def __init_subclass__(cls, *args, **kwargs):
if issubclass(cls, Addon):
raise TypeError("Do not mix Addons and CBlocks!")
super().__init_subclass__(*args, **kwargs)

class FBlock(CBlock):
pass

def test_addons():
try:
class CBlockWithAddon(CBlock, AddonDefault):
pass
except TypeError:
pass

blk = FBlock()
assert not isinstance(blk, AddonDefault), "TEST FAIL"
print("OK")

test_addons()

当我运行 python3 test.py我收到 TEST FAIL 异常。但是FBlock源自 CBlock源自 Block .怎么可能是AddonDefault的一个实例呢? ?


更新:我想强调的是,发布代码的唯一目的是演示我无法理解的行为。它是通过尽可能多地减少一个更大的程序来创建的。在这个过程中,它之前的任何逻辑都丢失了,所以请按原样处理,并关注为什么它给出了明显错误的答案。

最佳答案

不是完整的答案,而是一些提示。

似乎 CBlockWithAddon 仍然被视为 AddonDefault 的子类。例如。将两个打印语句添加到您的 test_addons():

def test_addons():
print(AddonDefault.__subclasses__())
try:
class CBlockWithAddon(CBlock, AddonDefault):
pass
except TypeError:
pass

print(AddonDefault.__subclasses__())
blk = FBlock()
assert not isinstance(blk, AddonDefault), "TEST FAIL"
print("OK")

结果

[]
[<class '__main__.test_addons.<locals>.CBlockWithAddon'>]
...
AssertionError: TEST FAIL

_py_abc tests for this :

    # Check if it's a subclass of a subclass (recursive)
for scls in cls.__subclasses__():
if issubclass(subclass, scls):
cls._abc_cache.add(subclass)
return True

这将在 cls=AddonDefaultsubclass=FBlockscls=CBlockWithAddon 时返回 True。

所以看起来有两件事出了问题:

  • 不正确创建的 CBlockWithAddon 仍被视为 AddonDefault 的子类。
  • 但 CBlockWithAddon 是以某种方式创建的,因此它似乎是 FBlock 的父类(super class)。

也许损坏的 CBlockWithAddon 实际上与 CBlock 相同,因此是 FBlock 的父类(super class)。

这对我来说已经足够了。也许它有助于您的调查。

(我不得不使用 import _py_abc as abc 进行分析。这似乎无关紧要。)


Edit1:我对 CBlockWithAddon 与其父类(super class) CBlock 的预感似乎是正确的:

CBWA = AddonDefault.__subclasses__()[0]
print(CBWA)
print(CBWA.__dict__.keys())
print(CBlock.__dict__.keys())
print(CBWA._abc_cache is CBlock._abc_cache)

给予

<class '__main__.test_addons.<locals>.CBlockWithAddon'>
dict_keys(['__module__', '__doc__'])
dict_keys(['__module__', '__init_subclass__', '__doc__', '__abstractmethods__', '_abc_registry', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version'])
True

因此 CBlockWithAddon 未正确创建,例如它的缓存注册表设置不正确。因此,访问这些属性将访问(第一个)父类(super class)的属性,在本例中为 CBlock。不太虚拟的 isinstance(self, CBlock) 将在创建 blk 时填充缓存,因为 FBlock 确实是 的子类>C block 。当调用 isinstance(blk, AddonDefault) 时,此缓存会被错误地重用。

我认为这回答了问题。现在下一个问题是:为什么 CBlockWithAddon 从未被正确定义而成为 CBlock 的子类?


Edit2:更简单的概念证明。

from abc import ABCMeta

class Animal(metaclass=ABCMeta):
pass

class Plant(metaclass=ABCMeta):
def __init_subclass__(cls):
assert not issubclass(cls, Animal), "Plants cannot be Animals"

class Dog(Animal):
pass

try:
class Triffid(Animal, Plant):
pass
except Exception:
pass

print("Dog is Animal?", issubclass(Dog, Animal))
print("Dog is Plant?", issubclass(Dog, Plant))

会导致

Dog is Animal? True
Dog is Plant? True

注意改变打印语句的顺序会导致

Dog is Plant? False
Dog is Animal? False

关于python - 为什么这个测试失败了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57848663/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com