python abc subclass 钩子在派生 class 时无效

python abc subclasshook has no effect when class is derived

当 class 派生自 class 并具有 __subclashook__ 实现时,无法从 issubclass return False。我修改了代码: 我只在两个 class 定义中添加了“(大小)”:

from abc import ABCMeta

class Sized(metaclass=ABCMeta):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if any("__len__" in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

class A(Sized):
    pass

class B(Sized):
    def __len__(self):
        return 0

print(issubclass(A, Sized))  # True - should be False
print(issubclass(B, Sized))  # True

这种情况下有什么办法可以return False吗?或者我做错了什么?

问题是当 __subclasshook__ 没有提前退出时,您 return NotImplemented。正如 documentation 中所述:

If it returns NotImplemented, the subclass check is continued with the usual mechanism.

所以它使用正常的子类检查,发现你做的,实际上是继承自Sized 所以returns True.

有两种解决方法:

  1. return False 而不是 return NotImplemented。然而,你真的want/needissubclass到returnFalsefor直接sub类吗?

  2. 如果您从 object 继承 类 AB 它会按预期工作:

    class A(object):
        pass
    
    class B(object):
        def __len__(self):
            return 0
    
    print(issubclass(A, Sized))  # False
    print(issubclass(B, Sized))  # True
    

我认为实现这个的好方法:

from abc import ABCMeta

class Sized(metaclass=ABCMeta):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if any("__len__" in B.__dict__ for B in C.__mro__):
                return True
            else:
                return False
        return NotImplemented


class A(Sized):
    pass


class B(Sized):
    def __len__(self):
        return 0


print(issubclass(A, Sized))  # False
print(issubclass(B, Sized))  # True

我认为当我们假设 abc 是类似于其他语言的编译(或反射)的机制时,我们应该 return False。如果怀疑 class 是正确的 subbclass 那么代码不应该 运行 甚至编译(不在 python 中)。