Python · 4 分钟阅读
`import` 语句
目录
- 1. 基本用法
- 2. 导入一个包
- 3. 相对导入
- 4. 模块搜索机制
- 5. 何时执行模块代码?
- 6. 循环引用
- 7. 条件导入 / 可选依赖
- 8. 重新加载(开发调试)
- 9. 性能:避免 import 时的重活
- 10. 最佳实践清单
- 11. 小结
import 是 Python 用来引入模块 / 包的语句:让其它文件里定义的函数、
类、变量在当前命名空间可用。本章覆盖 import 的所有写法、模块搜索机制、
__init__.py、包、相对导入、循环引用处理,以及工程实践。
1. 基本用法
1.1 导入整个模块
import math
print(math.sqrt(16)) # 4.0
print(math.pi)
推荐:保留模块前缀,让代码里能一眼看出
sqrt来自哪里。
1.2 导入特定名字
from math import pi, sqrt
print(pi) # 3.14159...
print(sqrt(16)) # 4.0
适合:高频使用、名字不会冲突。
当心:from xxx import *会污染命名空间、破坏可读性,几乎总是
坏味道。
1.3 别名
import numpy as np
import pandas as pd
from collections import defaultdict as ddict
as 让长模块名变短,让重名模块共存。
2. 导入一个包
包就是一个包含 __init__.py 的目录。可以嵌套:
myproject/
└── mypkg/
├── __init__.py
├── utils.py
└── io/
├── __init__.py
└── reader.py
import mypkg.utils # 引入模块
from mypkg.io import reader # 引入子包里的模块
from mypkg.io.reader import read_csv # 直接引入函数
Python 3.3+ 也支持隐式命名空间包(没有
__init__.py也能 import),
但仍建议每个包都加一个__init__.py,方便设置包级变量与__all__。
3. 相对导入
在包内部,用 . 表示“当前包”,.. 表示“上一级包”:
# 在 mypkg/io/reader.py 里:
from . import utils # mypkg.io.utils
from .. import config # mypkg.config
from ..utils import path_join # mypkg.utils.path_join
相对导入只能在包内模块使用;直接
python reader.py跑会抛
ImportError: attempted relative import with no known parent package。
4. 模块搜索机制
当你写 import mypkg 时,Python 按以下顺序查找:
- 内置模块(
sys.builtin_module_names) sys.path列表里各目录:- 当前脚本所在目录
PYTHONPATH环境变量里的目录- 标准库与第三方包的安装目录(
site-packages)
import sys
print(sys.path)
print(sys.builtin_module_names)
想知道
xxx真正从哪加载的?print(xxx.__file__)(标准库和 C 扩展可能是
None)。
5. 何时执行模块代码?
模块第一次被 import 时会自上而下执行整个文件,然后把得到的命名空间
缓存到 sys.modules。后续的 import 不会再执行,直接复用。
# config.py
print("loading config")
DEBUG = True
>>> import config # 第一次:打印 "loading config"
>>> import config # 第二次:什么都不做
想让某段代码“只在直接运行本文件时执行”,用:
if __name__ == "__main__": main()
6. 循环引用
# a.py
from b import b_func
def a_func(): ...
# b.py
from a import a_func # 💥 ImportError 或部分加载
def b_func(): ...
解决方案:
- 重构:把互相依赖的部分抽到第三个模块
c.py。 - 延迟导入:在函数内部再 import,运行期才解析:
def b_func(): from a import a_func # 用时再 import ... - 顶层 import 类型注解用
from __future__ import annotations(注解变成
字符串,运行时才解析),可以缓解部分问题。
7. 条件导入 / 可选依赖
try:
import ujson as json # 更快
except ImportError:
import json # 退回标准库
选 ujson / orjson / simplejson 时常用这招;注意:标准库
json已经
很快,第三方一般只在批量解析时才有明显收益。
8. 重新加载(开发调试)
import importlib
import mymodule
importlib.reload(mymodule)
生产环境不需要;交互式 REPL / Jupyter 里偶尔有用。
9. 性能:避免 import 时的重活
-
重活放函数里而不是模块顶层。
-
循环里 import 是大忌——每次都会查
sys.modules、建栈帧。 -
__all__列出公共 API:# mypkg/__init__.py from .utils import * __all__ = ["path_join", "load_yaml"] # 限定 `from mypkg import *`
10. 最佳实践清单
- ✅ 保留模块前缀(
import math而非from math import *)。 - ✅ 同名长名加别名(
numpy as np)。 - ✅ 集中 import 在文件顶部,避免在函数里 import(除非为了解决循环引用 / 可选依赖)。
- ✅ 写库时用
from . import sibling等相对导入;
写应用时用绝对导入。 - ✅ 给包加
__init__.py,哪怕是空的。 - ✅ 写
pyproject.toml/requirements.txt,别让 import 错误靠PYTHONPATH救场。 - ❌ 不要
from xxx import *。 - ❌ 不要在
__init__.py里做“启动服务、连接数据库、读全局配置”这类重活。
11. 小结
import是 Python 的模块系统入口;模块 / 包 / 命名空间包的层级都靠
__init__.py与sys.path支撑。- 优先绝对导入;包内用相对导入。
- 循环引用就用抽取 + 延迟导入。
- 工程上把依赖固化到
pyproject.toml/requirements.txt,不要依赖环境变量
PYTHONPATH来运行。