Python 在模块中使用isinstance

Python 在模块中使用isinstance,python,Python,我观察到一个涉及对象“AuditResult”的奇怪现象,希望有人能帮助我理解。我有一个模块设置如下: model\ __init__.py common.py (AuditResult is imported from audit.py into here) audit.py (where AuditResult lives) objects.py test.py 但是,如果使用model.common import AuditResult中的,代码将按预期

我观察到一个涉及对象“AuditResult”的奇怪现象,希望有人能帮助我理解。我有一个模块设置如下:

model\ __init__.py common.py (AuditResult is imported from audit.py into here) audit.py (where AuditResult lives) objects.py test.py 但是,如果使用model.common import AuditResult中的
,代码将按预期运行

>>> isinstance(audit_obj, AuditResult)
True
>>> type(audit_obj)
<class 'model.audit.AuditResult'>
>>> AuditResult
<class 'model.audit.AuditResult'>
>isinstance(审计对象、审计结果)
真的
>>>类型(审计对象)
>>>审核结果

问题在于:

from common import AuditResult
通用
模块作为“独立模块”导入,同时:

将其作为
模型的子模块导入
。Python区分了这两个模块!前者导入的
common
与后者导入的
common
之间没有关系。由于代码执行两次,
AuditResult
是不同的类,因此当混合它们时,
isinstance
正确返回
False

您可以通过以下方式进行测试:

import common
from model import common as common2
print(common is common2)
您将看到,这将打印
False
。如果模块不同,则其所有内容也不同,因此:

print(common.AuditResult is common2.AuditResult)
将打印
False

请记住,python不会对类型使用结构等价,也就是说,它不会检查“类本质上是相同的”
isinstance
只需检查与实例关联的类
是否正好是作为第二个参数传递的类。在您的案例中,两个
AuditResult
是不同的类,因此
audit.AuditResult
的实例是而不是
model.audit.AuditResult
的实例


事实上,这是我知道模块可以在哪里导入两次而不会弄乱导入机制的内部结构的唯一方法


我建议您避免子模块的绝对导入,即:

from common import AuditResult
如果要进行相对导入,请使用相对导入语法:

from .common import AuditResult

注意
(点)。这告诉python从当前包中导入
common

如果我错了,请纠正我,但我认为从common import AuditResult导入同一包中的模块的
方法在python3中已被删除?@JonasWielicki Yes和no。如果在pythonpath中添加目录,它将起作用。在python2中,当使用
-m
开关时,这些导入可以解释为相对导入。在python3中,除了使用显式语法外,所有导入都是绝对的。但是,将包目录添加到pythonpath不是一种坏做法吗?或者有理由这样做吗?非常感谢您的回答,我想我理解这个问题。唯一的问题是,当我尝试执行相对导入时,我得到了
ValueError:在非包中尝试相对导入
@jsexauer如果要运行包的子模块,请使用
-m
开关。例如
python-m model.common
。(您应该在
模型的父目录中)。使用
python模型/common.py
cd模型;python common.py
解释器将根本无法识别包,因此会出现错误。是的,将包添加到pythonpath是不好的做法。
from common import AuditResult
from .common import AuditResult