定义两个类(基类“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”(期望、预期)。
任何评论/建议/解释都会有所帮助。谢谢。
你在这里遇到的问题是由于
ClassA
在你的主脚本中定义,并且你有循环导入。实际上,您的脚本中涉及 三个 模块,而不是两个:
__main__
(定义__main__.ClassA
),导入...ClassB
(定义ClassB.ClassB
)导入...ClassA
(定义 ClassA.ClassA
与 __main__.ClassA
定义相同,但它是一个独特且独立的类)一个完全独立的主脚本副本,但以不同的名称独立导入,因此 __main__
相关行为不会t触发重要的是,
ClassB.ClassB
是从 ClassA.ClassA
继承的,但是 main
是针对 __main__.ClassA
的类型检查,一个完全不相关的类。
我已经讨论了为什么这不能像之前预期的那样工作,所以我将在这里针对您的具体情况简化答案:不要在任何循环导入中涉及
__main__
。它可以导入任何它喜欢的东西,但它不应该从中导入任何其他东西。在这种情况下,将 all 与 main
相关的行为移动到另一个脚本(不仅是导入守卫,还有 main
本身的定义),并让它从 ClassA
/ClassB
导入,而两者都不是从中导入,将解决您的问题。