Python模块包与迭代器_CSDN博客
Python 模块、包与迭代器:从导入机制到自定义迭代器,一文讲透
📌 本文系统梳理 Python 模块与包的导入机制(全局导入、局部导入、
__all__、__name__),以及迭代器的核心概念(可迭代对象 vs 迭代器、iter()/next()、自定义迭代器)。配合丰富代码示例,适合 Python 进阶学习者收藏参考。
📑 目录
一、模块基础
Python 中一个 .py 文件就是一个模块。模块让代码组织更清晰,避免命名冲突,实现代码复用。
1.1 全局导入
使用 import 导入整个模块,通过 模块名.成员 的方式访问:
# 语法:import 模块名 as 别名
import P_01_my_add as P_add
print(P_add.add(1, 2, 3)) # 6
print(P_add.num) # 100
1.2 局部导入
使用 from...import 导入模块中的指定成员,直接使用成员名:
# 语法:from 模块名 import 成员1 as 别名, 成员2 as 别名
from P_01_my_add import add, num as n2
from P_03_multi import multi, num as n1
print(add(2, 3, 4)) # 9
print(n2) # 100
print(multi(2, 3)) # 6
print(n1) # 200
导入方式对比:
| 方式 | 语法 | 访问方式 | 适用场景 |
|---|---|---|---|
| 全局导入 | import 模块名 | 模块名.成员 | 避免命名冲突 |
| 局部导入 | from 模块 import 成员 | 直接用成员名 | 频繁使用某成员 |
| 全部导入 | from 模块 import * | 直接用成员名 | 快速测试(不推荐生产) |
1.3 __all__ 属性
__all__ 控制 from 模块 import * 时哪些成员可以被导入:
# P_01_my_add.py
__all__ = ['num'] # 只允许通过 import * 导入 num
num = 100
_str1 = 'adc'
def add(a, b, c):
return a + b + c
# 使用 from import * 时
from P_01_my_add import *
print(num) # 100 ✅ 在 __all__ 中
# print(_str1) # ❌ NameError!不在 __all__ 中
# print(add) # ❌ NameError!不在 __all__ 中
💡 注意:
__all__只对from 模块 import *生效,对import 模块和from 模块 import 成员没有限制:import P_01_my_add as P_add print(P_add._str1) # ✅ 可以访问 from P_01_my_add import _str1 print(_str1) # ✅ 可以访问
1.4 __name__ 属性
__name__ 是一个特殊的内置属性,用于判断模块是被直接运行还是被导入:
| 场景 | __name__ 的值 |
|---|---|
| 文件被直接运行 | "__main__" |
| 文件被作为模块导入 | 模块名(如 "P_01_my_add") |
经典用法:保护测试代码
# P_01_my_add.py
__all__ = ['num']
num = 100
_str1 = 'adc'
def add(a, b, c):
return a + b + c
# 只在直接运行时执行测试代码,被导入时不执行
if __name__ == '__main__':
print(add(num, num, num)) # 300
🎯 为什么需要
__name__?如果不加
if __name__ == '__main__',当其他模块导入该模块时,测试代码也会被执行——这通常不是我们想要的。
二、dir() 内置函数
dir() 可以列出对象的所有属性和方法:
class Person:
home = 'earth'
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print('eating')
p1 = Person('zs', '18')
# 实例对象:包含类属性、实例属性、实例方法
print(dir(p1))
# 类对象:只包含实例方法和类属性(实例方法底层通过类调用)
print(dir(Person))
💡
dir()是探索未知模块/对象的利器,快速查看有哪些可用的属性和方法。
三、包(Package)
包是模块的集合,本质上是一个包含 __init__.py 文件的目录。
graphic/
├── __init__.py
└── circle.py
3.1 包的导入方式
# 方式1:全局导入(推荐)
import graphic.circle as c
print(c.PI)
# 方式2:通过 from 导入包中的模块
from graphic import circle
print(circle.PI)
3.2 __init__.py 的作用
__init__.py 是包的初始化文件,有两个主要作用:
- 标识目录为 Python 包(Python 3.3+ 可省略,但建议保留)
- 控制包的导入行为——在
__init__.py中声明导入,可以防止导入过多模块浪费资源
# graphic/__init__.py
# 在这里声明需要暴露的模块
from graphic import circle
# 使用时可以直接导入
import graphic
# graphic.circle 已可用
⚠️ 如果不声明,Python 默认不会导入包内的子模块,需要显式导入。
四、迭代器
4.1 可迭代对象 vs 迭代器
这是两个容易混淆的概念:
| 概念 | 说明 | 示例 | 判断方式 |
|---|---|---|---|
| 可迭代对象(Iterable) | 可以被 for 循环遍历的对象 | list、tuple、dict、set、str、生成器 | isinstance(obj, Iterable) |
| 迭代器(Iterator) | 实现了 __iter__() 和 __next__() 的对象 | iter() 的返回值 | isinstance(obj, Iterator) |
from collections.abc import Iterable, Iterator
# 常见容器是可迭代对象,但不是迭代器
print(isinstance([], Iterable)) # True
print(isinstance([], Iterator)) # False
print(isinstance((), Iterable)) # True
print(isinstance((), Iterator)) # False
print(isinstance({}, Iterable)) # True
print(isinstance({}, Iterator)) # False
print(isinstance(set(), Iterable)) # True
print(isinstance(set(), Iterator)) # False
# 生成器既是可迭代对象,也是迭代器
print(isinstance((x for x in range(10)), Iterable)) # True
print(isinstance((x for x in range(10)), Iterator)) # True
🎯 一句话区分: 可迭代对象可以被遍历,迭代器负责实际的遍历行为。所有迭代器都是可迭代对象,但反过来不成立。
4.2 使用迭代器:iter() 和 next()
迭代器的核心方法:
| 方法 | 作用 |
|---|---|
iter() | 将可迭代对象转换为迭代器 |
next() | 获取迭代器的下一个元素 |
list1 = [1, 2, 3]
it = iter(list1) # 将列表转换为迭代器
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
print(next(it)) # ❌ StopIteration(元素遍历完毕)
for 循环的底层原理:
# for item in [1, 2, 3]:
# print(item)
# 等价于:
it = iter([1, 2, 3]) # 调用 __iter__()
while True:
try:
item = next(it) # 调用 __next__()
print(item)
except StopIteration: # 元素遍历完毕,终止循环
break
💡
for循环会自动调用iter()获取迭代器,再通过next()逐个取元素,直到StopIteration异常终止。
4.3 自定义迭代器
实现 __iter__() 和 __next__() 两个方法,就能创建自定义迭代器:
class Reverse:
"""反向迭代器:从后往前遍历列表"""
def __init__(self, value):
self.value = value
self.index = len(value)
def __iter__(self):
return self
def __next__(self):
if self.index == 0:
raise StopIteration # 遍历完毕
else:
self.index -= 1
return self.value[self.index]
# 使用自定义迭代器
list1 = [1, 2, 3, 4, 5]
it = iter(Reverse(list1))
print(next(it)) # 5
print(next(it)) # 4
print(next(it)) # 3
# 也可以用 for 循环
for item in Reverse([1, 2, 3, 4, 5]):
print(item)
# 输出:5, 4, 3, 2, 1
自定义迭代器的工作流程:
1. 调用 iter(Reverse(list1))
→ 触发 __iter__(),返回 self
2. 调用 next(it)
→ 触发 __next__(),返回 self.value[self.index],index 减 1
3. 重复步骤 2 直到 index == 0
→ 触发 StopIteration,循环结束
💡 迭代器的核心优势: 惰性计算——不会一次性把所有数据加载到内存,而是用到哪个取哪个,适合处理大数据集。
五、总结
| 知识点 | 核心概念 | 关键语法 |
|---|---|---|
| 全局导入 | 导入整个模块 | import 模块 as 别名 |
| 局部导入 | 导入指定成员 | from 模块 import 成员 |
__all__ | 控制 import * 的导入范围 | __all__ = ['成员1', '成员2'] |
__name__ | 判断模块是否直接运行 | if __name__ == '__main__': |
dir() | 查看对象的所有属性和方法 | dir(对象) |
| 包导入 | 导入包中的模块 | import 包名.模块名 |
__init__.py | 包初始化,控制导入行为 | 在其中声明导入的模块 |
| 可迭代对象 | 可被 for 遍历的对象 | list、dict、str 等 |
| 迭代器 | 实现 __iter__ + __next__ | iter()、next() |
| 自定义迭代器 | 自己实现遍历逻辑 | 继承 __iter__ + __next__ |
模块与包的最佳实践:
- 用
__all__明确导出接口:隐藏内部实现,只暴露公开 API - 用
if __name__ == '__main__'保护测试代码:避免被导入时执行 - 包的
__init__.py按需导入:防止资源浪费 - 优先用
from 模块 import 成员:比import *更安全可控
迭代器的最佳实践:
- 区分可迭代对象和迭代器:前者可遍历,后者负责遍历
- 自定义迭代器必须实现
__iter__+__next__ StopIteration是迭代器的终止信号- 大数据场景优先用迭代器:惰性计算,节省内存
如果这篇文章对你有帮助,欢迎点赞 👍 收藏 ⭐ 评论 💬 支持一下!
更多推荐
所有评论(0)