Python 的 isinstance 方法结果对于子类实例是意外的

问题描述 投票:0回答:1

定义两个类(基类“ClassA”和子类“ClassB”在两个单独的文件中),在使用 Python 的 isinstance 方法时会产生意想不到的结果。输出似乎受到运行时使用的模块名称(名称空间?)的影响(__main__)。此行为出现在 Python 3.8.5 和 3.10.4 上。

文件 ClassA.py 包含:

class ClassA:
    def __init__(self, id):
        self.id = id
    def __str__(self) -> str:
        class_name = type(self).__name__
        return f"{class_name} WITH id: {self.id}"

def main():
    from ClassB import ClassB
    id = 42
    for i, instance in enumerate([ClassA(id), ClassB(id)]):
        label = f"{type(instance).__name__}:"
        print("#" * 50)
        print(f"{label}   type: {type(instance)}")
        label = " " * len(label)  # Convert label to appropriate number of spaces
        is_a = isinstance(instance, ClassA)
        is_b = isinstance(instance, ClassB)
        print(f"{label} is_a/b: {is_a}/{is_b}")
        print(f"{label}    str: {instance}")

if __name__ == "__main__":
    main()

文件ClassB.py包含:

from ClassA import ClassA

class ClassB(ClassA):
    def __init__(self, id):
        super().__init__(id)
        self.id *= -1

文件 main.py 包含:

if __name__ == "__main__":
    from ClassA import main
    main()

运行 ClassA.py 的输出为:

01: ##################################################
02: ClassA:   type: <class '__main__.ClassA'>
03:         is_a/b: True/False
04:            str: ClassA WITH id: 42
05: ##################################################
06: ClassB:   type: <class 'ClassB.ClassB'>
07:         is_a/b: False/True
08:            str: ClassB WITH id: -42

虽然运行 main.py(调用 ClassA.main)的输出给出:

01: ##################################################
02: ClassA:   type: <class 'ClassA.ClassA'>
03:         is_a/b: True/False
04:            str: ClassA WITH id: 42
05: ##################################################
06: ClassB:   type: <class 'ClassB.ClassB'>
07:         is_a/b: True/True
08:            str: ClassB WITH id: -42

注意 ClassA 实例的类型如何从“__main__.ClassA”(从 ClassA.py 运行时)更改(在第 02 行)到“ClassA.ClassA”(从 main.py 运行时)。同样,对 ClassA 和 ClassB 的 isinstance 类型检查(第 07 行)从“False/True”(意外)更改为“True/True”(期望、预期)。

任何评论/建议/解释都会有所帮助。谢谢。

python subclass program-entry-point isinstance
1个回答
2
投票

你在这里遇到的问题是由于

ClassA
在你的主脚本中定义,并且你有循环导入。实际上,您的脚本中涉及 三个 模块,而不是两个:

  1. 主脚本,名称为
    __main__
    (定义
    __main__.ClassA
    ),导入...
  2. ClassB
    (定义
    ClassB.ClassB
    )导入...
  3. ClassA
    (定义
    ClassA.ClassA
    __main__.ClassA
    定义相同,但它是一个独特且独立的类)一个完全独立的主脚本副本,但以不同的名称独立导入,因此
    __main__
    相关行为不会t触发

重要的是,

ClassB.ClassB
是从
ClassA.ClassA
继承的,但是
main
是针对
__main__.ClassA
的类型检查,一个完全不相关的类。

我已经讨论了为什么这不能像之前预期的那样工作,所以我将在这里针对您的具体情况简化答案:不要在任何循环导入中涉及

__main__
。它可以导入任何它喜欢的东西,但它不应该从中导入任何其他东西。在这种情况下,将 all
main
相关的行为移动到另一个脚本(不仅是导入守卫,还有
main
本身的定义),并让它从
ClassA
/
ClassB
导入,而两者都不是从中导入,将解决您的问题。

© www.soinside.com 2019 - 2024. All rights reserved.