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 是包的初始化文件,有两个主要作用:

  1. 标识目录为 Python 包(Python 3.3+ 可省略,但建议保留)
  2. 控制包的导入行为——在 __init__.py 中声明导入,可以防止导入过多模块浪费资源
# graphic/__init__.py

# 在这里声明需要暴露的模块
from graphic import circle
# 使用时可以直接导入
import graphic
# graphic.circle 已可用

⚠️ 如果不声明,Python 默认不会导入包内的子模块,需要显式导入。


四、迭代器

4.1 可迭代对象 vs 迭代器

这是两个容易混淆的概念:

概念说明示例判断方式
可迭代对象(Iterable)可以被 for 循环遍历的对象listtupledictsetstr、生成器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 遍历的对象listdictstr
迭代器实现 __iter__ + __next__iter()next()
自定义迭代器自己实现遍历逻辑继承 __iter__ + __next__

模块与包的最佳实践:

  1. __all__ 明确导出接口:隐藏内部实现,只暴露公开 API
  2. if __name__ == '__main__' 保护测试代码:避免被导入时执行
  3. 包的 __init__.py 按需导入:防止资源浪费
  4. 优先用 from 模块 import 成员:比 import * 更安全可控

迭代器的最佳实践:

  1. 区分可迭代对象和迭代器:前者可遍历,后者负责遍历
  2. 自定义迭代器必须实现 __iter__ + __next__
  3. StopIteration 是迭代器的终止信号
  4. 大数据场景优先用迭代器:惰性计算,节省内存

如果这篇文章对你有帮助,欢迎点赞 👍 收藏 ⭐ 评论 💬 支持一下!

更多推荐