添加新的单元测试后,在新测试之后,我开始在不相关的测试运行中失败。我不明白为什么。
我已将情况简化为以下代码。我仍然不知道发生了什么。我很惊讶地注释掉看似无关的代码行会影响结果:在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
。怎么可能是import io from 'socket.io-client';
const socket = io('http://xx.xxx.xx.xxx:4000');
componentDidMount(){
socket.on('connect', () => {
console.log(socket.connected);// connected is showing true
});
socket.on('messages', (data) => { // but here nothing happening. it is not coming inside function.
console.log(data);
});
}
的实例?
更新:我想强调的是,发布的代码的唯一目的是演示我无法理解的行为。它是通过尽可能减少一个更大的程序而创建的。在此过程中,以前存在的任何逻辑都丢失了,因此请按原样进行操作,并集中讨论为什么它给出明显错误的答案的问题。
答案 0 :(得分:3)
不是完整的答案,但有一些提示。
似乎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=AddonDefault
,subclass=FBlock
和scls=CBlockWithAddon
时,它将返回True。
所以看来有两件事出了错:
也许损坏的CBlockWithAddon实际上与CBlock相同,因此是FBlock的超类。
这对我来说已经足够了。也许可以帮助您进行调查。
(我必须使用import _py_abc as abc
进行此分析。这似乎无关紧要。)
Edit1:关于CBlockWithAddon
与其超类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
的创建不正确,例如其缓存注册表设置不正确。因此,访问这些属性将访问(第一个)超类的属性,在这种情况下为CBlock
。创建isinstance(self, CBlock)
时,不太合适的伪blk
将填充缓存,因为FBlock
实际上是CBlock
的子类。然后,在调用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
答案 1 :(得分:0)
为什么要使子类抽象而不是基类? 这背后有某种逻辑吗?
如果将抽象向上移动一层,它将按预期工作,否则将类型和abc元类混合使用:
import abc
class Addon(metaclass=abc.ABCMeta):
pass
class AddonDefault(Addon):
pass
class Block(metaclass=abc.ABCMeta):
def __init__(self):
isinstance(self, CBlock)
class CBlock(Block):
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()