跳至正文

梳理python的import

  • 随笔

最近公司出了一个bug,我有点不太懂python循环import会发生什么,也不知道python是怎么处理的,周末顺着文档缕了一遍python的导入系统。

官方导入系统的文档:https://docs.python.org/zh-cn/3.12/reference/import.html
本文主要是记录一些细节、自己的理解以及一些注解。

基本介绍

import

import: import语句包含了两个操作:

  1. 它先搜索指定的模块,如果不存在会创建改模块;
  2. 然后将搜索结果绑定到当前作用域中;

展开来说,就是import首先会调用__import__将模块拿到引用,然后import会处理名字绑定相关的操作,类似于

import math         # 将math模块绑定到locals()的math
import abc as a     # 将abc模块绑定到locals()的a
from abc import d       # 将abc.a绑定到locals()的d上

__import__

看完前面的,__import__就很清晰了,它在设计上是仅用来做搜索以及在没搜到时创建模块的功能的,但是虽然不会影响globals()和locals(),但是会修改sys.modules。

模块和包

模块:Python只有一种模块对象类型,所有模块都属于该类型,无论是从python还是C++实现的类模块。
包:可以将模块理解成文件,包是目录,包可以拥有子包,也可以拥有模块。

包又分为常规包和命名空间包,常规包就是目录有__init__.py的包,命名空间包就可以让一个包内的模块不在同一个目录,甚至

注:

  • 包是为了帮助组织模块并提供名称层次结构,Python 还引入的概念。
  • 所有的包也都是模块
  • 拥有__path__的模块会被当做是包

加载模块

[原文]
当一个模块说明被找到时,导入机制将在加载该模块时使用它(及其所包含的加载器)。 下面是导入的加载部分所发生过程的简要说明:

module = None
if spec.loader is not None and hasattr(spec.loader, 'create_module'):
    # It is assumed 'exec_module' will also be defined on the loader.
    module = spec.loader.create_module(spec)
if module is None:
    module = ModuleType(spec.name)
# The import-related module attributes get set here:
_init_module_attrs(spec, module)

if spec.loader is None:
    # unsupported
    raise ImportError
if spec.origin is None and spec.submodule_search_locations is not None:
    # namespace package
    sys.modules[spec.name] = module
elif not hasattr(spec.loader, 'exec_module'):
    module = spec.loader.load_module(spec.name)
else:
    sys.modules[spec.name] = module
    try:
        spec.loader.exec_module(module)
    except BaseException:
        try:
            del sys.modules[spec.name]
        except KeyError:
            pass
        raise
return sys.modules[spec.name]

循环import的问题

主要文章: https://www.jb51.net/article/51815.htm

# 文件A.py:
from B import D
class C: pass

# 文件B.py
from A import C
class D: pass

这个过程会直接报错,但是如果将from A import C改成import A就不会报错,这是为什么呢?
可以看到前面的文档里的内容,包括这段开头的文章。
在import时首先查看是否存在这个模块,如果存在,则直接获取,如果不存在,则先创建一个空的占用,然后再执行loader.execute()

两个连续的from xx import xx发生的事情就是,在第二次执行from A import C时,会发现sys.modules里面已经有A,那么import做的事情是获取A.C,然后把引用放进globals。

但是这个A是个空的模块,所以就Trace了。

End

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

目录