Python模块__name__在不同导入方式下的定义机制解析
Python模块__name__属性的定义与导入行为解析
核心定义
Python中每个模块的__name__属性,是模块被导入时解释器赋予的唯一标识符,本质是模块在sys.modules字典中的键名,用于区分不同模块实例:
- 当模块作为主程序直接运行时,
__name__会被设为__main__ - 当模块被导入时,
__name__是其导入路径的完全限定名(从顶层包到当前模块的层级路径)
场景与代码还原
目录结构
├ main.py └ test/ ├── __init__.py ├── a │ ├── a1.py │ ├── base.py # a1.py和b1.py会导入它 │ └── __init__.py └── b ├── b1.py └── __init__.py
各文件代码
test/a/base.py
# test/a/base.py print("Base.py be imported, __name__ =", __name__)
test/a/a1.py
# test/a/a1.py print("[a1.py] import base: start") from . import base print("[a1.py] import base: end")
test/a/init.py
# test/a/__init__.py from . import base from .a1 import *
test/b/b1.py
# test/b/b1.py print("[b1.py] import base: start") from test.a import base print("[b1.py] import base: end")
test/b/init.py
# test/b/__init__.py from test.b.b1 import *
test/init.py的两种导入方式
# method1 print(" ----- import a -----") from .a import * print(" ----- import b -----") from .b import * # method2 import sys import os sys.path.append(os.path.dirname(os.path.abspath(__file__)) + "/./") print(" ----- import a -----") from a import * print(" ----- import b -----") from b import *
两种导入方式的输出差异
方法1输出
----- import a ----- Base.py be imported!, __name__ = test.a.base [a1.py] import base: start [a1.py] import base: end ----- import b ----- [b1.py] import base: start [b1.py] import base: end
方法2输出
----- import a ----- Base.py be imported!, __name__ = a.base [a1.py] import base: start [a1.py] import base: end ----- import b ----- [b1.py] import base start Base.py be imported!, __name__ = test.a.base [a1.py] import base: start [a1.py] import base: end [b1.py] import base: end
差异原因与底层原理
1. __name__的赋值逻辑
Python解释器处理导入时的核心步骤:
- 解析导入语句,确定模块的搜索路径和限定名
- 如果模块不在
sys.modules中,加载模块代码、创建模块对象,将__name__设为解析出的限定名,再存入sys.modules - 如果模块已在
sys.modules中,直接复用已有模块对象,不会重新设置__name__
2. 方法1的逻辑(相对导入+包内层级)
方法1使用相对导入(from .a import *),此时test是顶层包,a是test的子包:
- 导入
test.a时,base.py的限定名是test.a.base,存入sys.modules['test.a.base'] - 后续
b1.py用绝对导入from test.a import base时,直接复用sys.modules中已有的模块实例,因此不会重复打印base.py的输出
3. 方法2的逻辑(修改sys.path+直接导入子包)
方法2把test/目录加入sys.path,此时a和b被当作顶层包处理:
- 导入
a时,base.py的限定名是a.base,存入sys.modules['a.base'] - 后续
b1.py用绝对导入from test.a import base时,解析出的限定名是test.a.base,该键不在sys.modules中,因此会重新加载base.py,此时__name__被设为test.a.base,导致重复打印输出
4. 导入方式对__name__的影响
- 相对导入:仅能在包内部使用,模块的
__name__会继承当前包的层级,形成完整限定名(如test.a.base) - 绝对导入:根据导入时的顶层包路径生成限定名,若搜索路径变化,顶层包的识别会改变,
__name__也会随之变化
关键结论
- 导入方式本身不直接改变
__name__,但导入时的搜索路径(sys.path)和模块的层级定位决定了__name__的取值 - 同一个物理文件,只要被解析成不同的限定名(比如
a.base和test.a.base),就会被当作两个独立模块存入sys.modules,各自拥有独立的__name__
内容的提问来源于stack exchange,提问作者Chun-Ye Lu
相关产品推荐
相关产品推荐

