python踩坑记录之import和module

发布时间 2023-11-19 11:21:27作者: 强梁

1.问题重现

最近开发时需要将一个别人的python项目作为submodule引入,调用的时候遇到了奇怪的问题,最后定位到问题是import导致的。首先对问题做一个说明。

项目结构如下:

Project/
    main.py
    submodule/
        __init__.py
        handler.py
        tools.py

导致问题的代码部分(对代码进行了简化)如下:

  • main.py
import sys
import os
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/submodule")
from submodule.handler import Handler
from submodule.tools import check

if __name__ == "__main__":
    handler = Handler()
    print("check result:{}".format(check(handler)))
  • handler.py
class Handler:
    pass
  • tools.py
import sys
import os
from handler import Handler

def check(data):
    return type(data) == type(Handler())

执行main.py后,输出的结果是:check result:False,而我期望的结果是True

2.问题分析

很明显,上述结果说明传递进去的dataHandler()生成的实例类别不同,但这看起来很不合理:我明明import的是同一个类,为什么实例化之后判断类别会不同呢。将两个实例的类别打印出来发现:

type of data:<class 'submodule.handler.Handler'>
type of Handler():<class 'handler.Handler'>

可以看到两个实例的类别并不一样,并且很容易就可以发现,两个实例类比中Handler的前缀刚好就是两个文件中import时候的路径,因此大概率是这个问题导致的(实际上也确实是)。

3.原理

3.1 python的import机制及module

导致这个问题的核心原因,在于python的import机制。当我们import一个module时,导入的并不是某个类的定义或者一段代码,而是一个实例化了一个module类:

>>> import submodule.handler as handler
>>> type(handler)
<class 'module'>

而所有的module,都被维护在一个大的字典sys.modules中:

>>> import sys
>>> print(sys.modules)
{
    'sys': <module 'sys' (built-in)>,
    'builtins': <module 'builtins' (built-in)>,
    '_frozen_importlib': <module '_frozen_importlib' (frozen)>,
    ......
}

其中的key就是我们import时候的名称或者说路径。在main.py中打印sys.modules,可以看到:

'submodule.handler': <module 'submodule.handler' from '/home/aabb/import_test/submodule/handler.py'>,
'handler': <module 'handler' from '/home/aabb/import_test/submodule/handler.py'>

尽管import“指向”了同一个文件,但实际上是两个不同的module,这两个module中的Handler类也并不是同一个类(准确来说是对象。python中,类本身也是一个对象,可以参考python中类对象),因此其实例化的对象类型肯定不会相同。

3.2 解决方案

知道了上述原理,解决方案也就有了,那就是在项目内要统一import的路径。比较合理的方式是无论在项目内部还是项目外部,都从项目的根目录开始引用。我这个项目中的问题实际上是submodule中没有从根目录开始引用(不过人家这个项目本来也不是让别人拿来当submodule用的)

4. 后记

菜鸡的第一篇博客,问题也是个很简单的问题,不过我觉得是个很容易被忽略的问题(也可能是我太菜了_(:з)∠)_)。很早前就一直有想写博客的想法,但一直没有迈出第一步,这次也算开了个头。

如果有哪些地方写的有问题还请大佬们指正_(:з)∠)_