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 of
    sub.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