You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 21:43:19