Python篇---import
在 Python 中,import
是用于加载模块(module)或包(package)中定义的对象(如函数、类、变量等)的核心语句。其底层逻辑涉及模块查找、加载、缓存等过程,而导入的 "参数" 本质上是指可以通过 import
语句获取的各类对象(模块、函数、类等)以及相关的语法选项。
一、import
的核心逻辑:模块的查找与加载
当执行 import
语句时,Python 解释器会按以下步骤处理:
1. 检查模块是否已加载(缓存机制)
Python 会先检查 sys.modules
字典(模块缓存),如果目标模块已被导入过,会直接从缓存中获取,避免重复加载。
例如:首次 import math
会加载 math
模块,再次 import math
会直接使用缓存中的 math
模块。
2. 查找模块(核心步骤)
如果模块未加载,Python 会按 sys.path
列表中的路径顺序查找模块文件。sys.path
包含以下路径(优先级从高到低):
- 当前执行脚本所在的目录(或交互式环境的当前工作目录);
- 环境变量
PYTHONPATH
中指定的路径; - Python 标准库的安装路径(如
site-packages
); - 第三方库的安装路径(通过
pip
安装的库通常在这里)。
模块文件的格式可以是:
.py
源码文件;.pyc
编译后的字节码文件;- 目录(作为包,需包含
__init__.py
文件,Python 3.3+ 后可选,但推荐保留以明确标识为包); - 动态链接库(如
.so
、.pyd
等,通常是 C 扩展模块)。
3. 加载并执行模块
找到模块文件后,Python 会执行模块中的代码(顶层语句),并将模块对象存入 sys.modules
中。模块中的变量、函数、类等会成为模块对象的属性,供外部访问。
二、import
可导入的对象与语法
import
语句的核心是 "导入指定对象",这些对象可以是模块、包,也可以是模块 / 包中的具体成员(函数、类、变量等)。常见语法及可导入的 "参数" 如下:
1. 导入整个模块
语法:import 模块名 [as 别名]
作用:导入整个模块,并将其作为一个对象绑定到当前作用域的变量(模块名或别名)上。
示例:
import math # 导入标准库math模块
import pandas as pd # 导入第三方库pandas,并重命名为pd(别名)print(math.pi) # 访问模块中的变量
print(pd.DataFrame()) # 通过别名访问模块中的类
这里的 "参数" 是模块名,可选指定别名(通过 as
关键字),简化后续调用。
2. 从模块中导入特定成员
语法:from 模块名 import 成员名 [as 别名]
作用:直接导入模块中的某个成员(函数、类、变量等),无需通过模块名前缀访问。
示例:
from math import pi # 导入math模块中的pi变量
from math import sqrt as square_root # 导入sqrt函数,并重命名为square_root
from datetime import datetime # 导入datetime模块中的datetime类print(pi) # 直接使用成员,无需math.前缀
print(square_root(4)) # 通过别名使用函数
这里的 "参数" 是模块名和成员名,成员可以是函数、类、变量等,同样支持通过 as
指定别名。
3. 从模块中导入所有成员(不推荐)
语法:from 模块名 import *
作用:导入模块中所有未被 _
前缀标记的成员(或 __all__
列表中指定的成员)。
示例:
from math import * # 导入math模块中所有公开成员print(pi) # 直接使用pi
print(sin(0)) # 直接使用sin函数
注意:
- 不推荐使用,可能导致命名冲突(不同模块的同名成员会覆盖);
- 模块可通过
__all__
变量控制*
导入的成员,例如:# math.py 中 __all__ = ["pi", "sin"] # 仅允许*导入pi和sin
4. 导入包(包含多个模块的目录)
包是包含多个模块的目录(通常含 __init__.py
文件),导入包的逻辑与模块类似,但更灵活:
导入整个包:
import 包名 [as 别名]
此时会执行包的__init__.py
文件,可通过包名访问其下的模块。从包中导入模块:
from 包名 import 模块名 [as 别名]
从包的模块中导入成员:
from 包名.模块名 import 成员名
示例(假设有如下包结构):
mypackage/__init__.pymodule1.py # 含函数 func1()subpackage/module2.py # 含类 Class2
导入方式:
import mypackage # 导入包,执行__init__.py
from mypackage import module1 # 从包中导入module1模块
from mypackage.module1 import func1 # 从包的模块中导入函数
from mypackage.subpackage.module2 import Class2 # 导入子包的模块成员
5. 相对导入(包内部使用)
在包的内部模块中,可使用相对路径导入同一包内的其他模块,语法为 .
(当前目录)或 ..
(父目录):
示例(在 mypackage/module1.py
中导入同包的 module2.py
):
from . import module2 # 导入同目录下的module2
from .subpackage import module3 # 导入子目录下的module3
from .. import parent_module # 导入父目录下的parent_module(需确保层级正确)
注意:相对导入只能在包内部的模块中使用,不能在顶层脚本(直接执行的 .py
文件)中使用。
三、import
相关的高级机制
动态导入:通过
importlib
模块动态加载模块,适用于需要运行时决定导入哪个模块的场景:import importlib math_module = importlib.import_module("math") # 等价于 import math print(math_module.pi)
模块重载:默认情况下,模块加载后不会重新执行,可通过
importlib.reload()
强制重载:import importlib import mymodule importlib.reload(mymodule) # 重新执行mymodule.py并更新模块对象
__init__.py
的作用:- 标识目录为包;
- 控制包的导入行为(如定义
__all__
控制from 包 import *
导入的模块); - 执行包的初始化代码(如批量导入子模块)。
四、常见错误与注意事项
ImportError
:通常是模块未找到(检查sys.path
是否包含模块路径)或导入的成员不存在。- 循环导入:两个模块相互导入(如
a.py
导入b.py
,b.py
又导入a.py
),会导致部分成员未定义,需通过调整代码结构避免。 - 命名冲突:不同模块的同名成员被导入时,后导入的会覆盖先导入的,推荐使用模块名前缀或别名避免。
总结:Python 的 import
逻辑围绕 "模块查找 - 加载 - 缓存" 展开,可导入的对象包括模块、包、函数、类、变量等,通过不同的语法(import
/from ... import
/as
等)灵活控制导入行为,满足不同场景的需求。