Python继承检测是如何工作的?
我有一个基类和几个继承自它的子类。我试图动态地检测哪些子类从基类继承。我目前正在通过动态导入基类Python继承检测是如何工作的?,python,inheritance,python-import,Python,Inheritance,Python Import,我有一个基类和几个继承自它的子类。我试图动态地检测哪些子类从基类继承。我目前正在通过动态导入基类\uuuu init\uuuuuu()中的所有子类,然后使用\uuuu子类
\uuuu init\uuuuuu()
中的所有子类,然后使用\uuuu子类
方法来实现这一点
我有以下文件结构:
proj/
|-- __init__.py
|-- base.py
`-- sub
|-- __init__.py
|-- sub1.py
|-- sub2.py
`-- sub3.py
base.py:
import importlib
class Base(object):
def __init__(self):
importlib.import_module('sub.sub1')
importlib.import_module('sub.sub2')
importlib.import_module('sub.sub3')
@classmethod
def inheritors(cls):
print(cls.__subclasses__())
b = Base()
b.inheritors()
sub1.py:
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from base import Base
class Sub1(Base):
pass
sub2.py:
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from base import Base
class Sub2(Base):
pass
最后是sub3.py:
import sys
import os
class Sub3(object):
pass
您会注意到sub.sub1.sub1
和sub.sub2.sub2
都继承自base.base
,而sub.sub3.sub3
则不继承
当我打开IPython3并运行import base
时,我得到以下输出:
In [1]: import base
[<class 'sub.sub1.Sub1'>, <class 'sub.sub2.Sub2'>]
[1]中的:导入基
[, ]
上面的输出完全符合我的预期。当我使用Python命令行运行base.py时,它变得很奇怪:
python3 base.py
[<class 'sub.sub2.Sub2'>]
[]
python3 base.py
[]
[]
现在我想我知道在第二种情况下有两个打印,因为Python导入器最初没有在sys.modules
全局变量中看到base.py
,所以当导入子类时,它将再次导入base.py
,代码将再次执行。此解释没有解释为什么它第一次打印[]
,而不是将[]
作为sub.sub1.sub1
首先导入,也没有解释为什么只有sub.sub2.sub2
显示在子类中而sub.sub1.sub1
不显示
任何有助于我理解Python在这方面如何工作的解释都将不胜感激
编辑:我想使用python base.py运行该模块,因此也许可以为我指出正确的方向?你打了个结。
一个复杂的、没有缝的结。我可以弄明白——但我不知道我是否能记住它,以清晰的方式解释正在发生的事情:——)
但首先有一件事:这与“继承检测”关系不大,而与导入系统关系不大——这是一个复杂的结
因此,您得到了意外的结果,因为当您执行python base.py
时,base的内容被记录为sys.modules
中名为\uuuuu main\uuuu
的模块。
通常,Python永远不会导入模块并再次运行相同的代码:在fid一个import语句尝试导入现有模块时,它只会创建一个指向现有模块的新变量。如果该模块尚未完成其主体的执行,则并非所有类或变量都会出现在第二条import语句所在的位置。对importlib的调用没有更好的效果——它们只是不能自动执行可变投标部分。当您执行循环导入、更改导入路径并从另一个文件导入名为base
的模块时,Python不知道这是base
,即\uuu main\uu
。因此,新的模块将获得一个新的新导入,以及sys.modules中的第二个条目,如base
如果您只是在继承器方法中打印\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu类
,则会很清楚:
@classmethod
def inheritors(cls):
print("At class {}. Subclasses: {}".format(__class__, cls.__subclasses__()))
然后您将看到“base.base”有“sub2”子类,而\uuuuu main\uuuu.base
没有子类
现在,让我试着为它设定一个时间表:
base.py
作为\uuuuu main\uuuuu
导入,并运行到b行=
Base()
。此时,Base的\uuuu init\uuuu
方法将导入
子模块
子模块sub1
运行,更改sys.path,然后
重新导入base.py作为base
模块李>
报告的内容
基本模块一直运行,直到满足base.base中的\uuuu init\uuuu
方法;
其中,它导入sub.sub1
,Python发现这个模块
已导入,并且位于系统模块中。它的代码还没有被修改
已完成,但尚未定义Sub1
base李>
在base的sub1导入中,\uuuu init\uuu
尝试导入sub.sub2
。那个
是Python的新模块,因此将其导入
关于进口
sub2
,当满足import base
时,Python将模块识别为
已导入(尽管并非所有初始化代码都已导入
已完成)-它只是将名称别名带到sub2全局变量,并且
继续
Sub2被定义为base.base的子类
sub.sub2
导入完成,Python恢复到步骤(4)上的\uuuu init\uuuu
方法;Python导入sub.sub3并继续执行b.inheritors()
调用
(从base
,而不是从main
)。在这一点上,唯一的子类
base.base
是打印的sub2
进口
base.py
当base
完成时,Python继续执行bodu
ofsub.sub1
-类sub1
定义为base.base
Python恢复\uuuuu main\uuuuuu.base.\uuuuu init\uuuu
执行,导入
sub.sub2-但它已运行,与sub.sub3
\uuuuu main\uuuuuuu.Base.heritators
在\uuuuuuu main\uuuuuuu
中调用,并打印no
子类
这是一段复杂历史的结束
你应该做什么
第一:如果您需要进行sys.path.append
欺骗,那么您的包有问题。让您的包成为proj
,并指向proj.\uuuu init\uuu
以导入base
,如果您希望运行该程序(并动态导入其他模块),但停止摆弄sys.path以在您自己的包中查找内容
第二:
cls.\uuuuu subclasses\uuuuu
调用没有什么用处,因为它只会告诉您关于cls
的imediate子类-如果有一个grand chid子类,它将不加注释
mo