抽象类被 meta class 继承导致其 isinstance 与 issubclass 检查失效

本文探讨了Python中使用元类时遇到的issubclass方法异常问题,特别是当涉及__subclasses__方法时出现的错误,并提供了解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Python 3.10.4 (main, Mar 31 2022, 03:37:37) [Clang 12.0.0 ] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from abc import ABCMeta
>>> class A(metaclass=ABCMeta): pass
... 
>>> issubclass(int, A)
False
>>> class Meta(type, A): pass
... 
>>> issubclass(str, A)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/lutingwang/anaconda3/envs/todd/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
  File "/Users/lutingwang/anaconda3/envs/todd/lib/python3.10/abc.py", line 123, in __subclasscheck__
    return _abc_subclasscheck(cls, subclass)
TypeError: unbound method type.__subclasses__() needs an argument

正常来说, issubclass 应该返回 False 。但是在定义了 Meta 之后, issubclass 开始报错。两次调用 issubclass 的对象分别是 intstr ,是为了绕过 issubclass 的缓存机制。

造成这一现象的原因是, issubclass 需要递归遍历 A 的所有子类,这依赖于的 __subclasses__ 方法。通过报错信息不难看出,问题的关键在于 __subclasses__ 被错误调用。因此我们怀疑, Meta.__subclasses__ 导致了报错。为了验证猜想,我们编写了以下测试代码

>>> Meta.__subclasses__()
Traceback (most recent call last):
  File "/Users/lutingwang/Developer/todd/tmp.py", line 13, in <module>
    Meta.__subclasses__()
TypeError: unbound method type.__subclasses__() needs an argument

报错信息一致,说明我们的猜想是正确的。因为 Meta 中没有定义 __subclasses__ 方法,所以在调用 Meta.__subclasses__ 时会自动解析到 type.__subclasses__

>>> Meta.__subclasses__
<method '__subclasses__' of 'type' objects>

但是这里的 Meta.__subclasses__Meta 从基类 type 继承的方法,而不是 meta class type 实现的方法。二者的区别在于,继承而来的方法是未绑定的,而 meta class 实现的方法是绑定的。为了解决这一问题,需要重载 Meta.__subclasses__ ,使之可以处理未绑定的情况。

class Meta(type, A):

    def __subclasses__(self=...) -> List['Meta']:
        if self is ...:
            return type.__subclasses__(Meta)
        return super().__subclasses__()

__isinstance__ 底层调用了 __issubclass__ ,因此上述分析同样适用。

如果 Meta 进一步被继承,那么在子类中也需要重载

class SubMeta(Meta, A):

    def __subclasses__(self=...) -> List['SubMeta']:
        if self is ...:
            return type.__subclasses__(SubMeta)
        return super().__subclasses__()

否则, SubMeta.__subclasses__ 将会解析到 Meta.__subclasses__ ,然后递归调用 SubMeta.__subclasses__ 最终导致递归次数过多,有时表现为段错误。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LutingWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值