卡码笔记-最强八股文
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
首页
计算机基础
Python
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
大模型Github (opens new window)
代码随想录 (opens new window)
  • 本栏必读

    • Python面试题专栏介绍
    • Python学习路线
    • Python零基础入门课
  • 基础语法

  • 函数、作用域与模块

    • Python函数参数是值传递还是引用传递?
    • 位置参数、关键字参数、*args和**kwargs怎么用?
    • Python默认参数为什么不能随便使用列表和字典?
    • Python中的匿名函数、map、filter和reduce怎么用?
    • Python的LEGB作用域是什么?
    • global和nonlocal有什么区别?
    • 什么是闭包?闭包中的变量为什么会出现延迟绑定?
    • Python模块和包是什么?import是怎么工作的?
      • 简要回答
      • 详细回答
      • 知识拓展
  • 面向对象

  • 进阶语法与语言机制

# Python模块和包是什么?import是怎么工作的?

面试官问:"Python的模块和包有什么区别?import是如何查找和加载模块的?"

很多人只知道"模块是.py文件,包是包含__init__.py的目录",但说不清楚import的查找顺序、模块缓存、循环导入的原因,以及from import和import的区别。

# 简要回答

模块是一个.py文件,包含Python定义和语句。模块可以定义函数、类和变量,也可以包含可执行代码。

包是包含__init__.py文件的目录,用于组织多个模块。包可以嵌套,形成层级结构。

import执行时,Python按sys.path顺序查找模块,找到后编译并执行模块代码,将模块对象缓存到sys.modules。后续import直接从缓存返回,不会重复执行。

import module导入模块对象,from module import name导入模块中的名字。前者访问时需要module.name,后者直接用name。

# 详细回答

# 模块

模块是一个.py文件,文件名去掉.py就是模块名:

# math_utils.py
def add(a, b):
    return a + b

PI = 3.14159
1
2
3
4
5

导入模块:

import math_utils

print(math_utils.add(2, 3))  # 5
print(math_utils.PI)         # 3.14159
1
2
3
4

# 包

包是包含__init__.py的目录,用于组织多个模块:

my_package/
    __init__.py
    module_a.py
    module_b.py
1
2
3
4

导入包中的模块:

import my_package.module_a
from my_package import module_b

my_package.module_a.func()
module_b.func()
1
2
3
4
5

__init__.py可以是空文件,也可以包含包的初始化代码。Python 3.3+支持隐式命名空间包(没有__init__.py),但显式包仍然是推荐做法。

# import的查找顺序

import module_name时,Python按以下顺序查找:

  1. sys.modules缓存:已导入的模块直接返回。
  2. 内置模块:如sys、os、math。
  3. sys.path列表中的路径,依次查找:
    • 当前脚本所在目录(或交互式环境的当前目录)。
    • PYTHONPATH环境变量中的目录。
    • 标准库目录。
    • 第三方库目录(site-packages)。

找到模块后,Python编译为字节码(如果需要),执行模块代码,创建模块对象并缓存到sys.modules。

# 查看sys.path

import sys
print(sys.path)
# ['当前目录', '/usr/lib/python3.x', '/usr/lib/python3.x/site-packages', ...]
1
2
3

可以动态修改sys.path,但不推荐:

sys.path.append('/custom/path')
import custom_module
1
2

# import与from import

# import导入模块对象
import math
print(math.sqrt(16))  # 4.0

# from import导入模块中的名字
from math import sqrt
print(sqrt(16))  # 4.0

# 导入多个名字
from math import sqrt, pi

# 导入所有名字(不推荐)
from math import *
1
2
3
4
5
6
7
8
9
10
11
12
13

from math import *会污染命名空间,隐藏了名字来源,降低可读性。

import查找与加载流程

# 模块缓存sys.modules

模块只会被导入一次,后续import直接从缓存返回:

# first.py
print("first.py is being imported")

# main.py
import first  # first.py is being imported
import first  # 不会再次打印,直接从缓存返回
1
2
3
4
5
6

可以查看缓存:

import sys
import math

print('math' in sys.modules)  # True
print(sys.modules['math'])    # <module 'math' from '...'>
1
2
3
4
5

强制重新加载模块(不推荐):

import importlib
importlib.reload(math)
1
2

# 相对导入与绝对导入

绝对导入:从sys.path根开始查找:

from my_package.module_a import func
1

相对导入:从当前包的位置开始查找:

# my_package/module_b.py
from . import module_a         # 导入同级模块
from .module_a import func     # 导入同级模块中的名字
from .. import parent_module   # 导入上级包中的模块
1
2
3
4

相对导入只能在包内使用,不能在顶层脚本中使用。

# 循环导入问题

两个模块互相导入会导致循环导入:

# a.py
import b
def func_a():
    b.func_b()

# b.py
import a
def func_b():
    a.func_a()
1
2
3
4
5
6
7
8
9

Python允许循环导入,但如果导入时立即访问对方模块的名字,会出错:

# a.py
import b
print(b.var)  # AttributeError: module 'b' has no attribute 'var'

# b.py
import a
var = 10
1
2
3
4
5
6
7

导入a时,a导入b,b导入a(此时a还没执行完),b执行完后返回a,a继续执行print(b.var)。此时b已经完成,b.var存在,不会出错。但如果a在导入时就访问b的名字,而b还没定义,就会出错。

解决方法:

  1. 延迟导入:在函数内导入。
  2. 重构代码:把共同依赖提取到第三个模块。
  3. 导入模块而不是名字:import b而不是from b import func。

# __name__变量

每个模块有__name__变量,表示模块名:

# module.py
print(__name__)  # 'module'

# main.py
import module  # 输出 'module'
1
2
3
4
5

当模块作为脚本直接运行时,__name__是'__main__':

# script.py
if __name__ == '__main__':
    print("This is the main script")
1
2
3

这是Python的常见模式:模块既可以被导入,也可以作为脚本运行。

# 知识拓展

Q1:__init__.py里应该写什么?

可以写包的初始化代码、导入子模块、定义__all__等:

# my_package/__init__.py
from .module_a import func_a
from .module_b import func_b

__all__ = ['func_a', 'func_b']
1
2
3
4
5

__all__定义了from my_package import *会导入哪些名字。

Q2:import *会导入所有名字吗?

如果模块定义了__all__,只导入__all__中的名字;否则导入所有不以_开头的名字。

Q3:可以导入.pyc文件吗?

可以。Python会优先使用.pyc字节码文件(在__pycache__目录中),避免重复编译。

Q4:为什么不推荐修改sys.path?

修改sys.path会影响全局导入行为,降低代码可移植性。更好的做法是用虚拟环境、正确的包结构或安装包到site-packages。

Q5:如何查看模块的搜索路径?

import module_name
print(module_name.__file__)  # 模块文件路径
1
2

对于内置模块,__file__可能不存在。

Last Updated: 10/13/2026, 5:57:00 PM

← 什么是闭包?闭包中的变量为什么会出现延迟绑定? Python中的类和对象是什么? →

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪