欢迎光临
我们一直在努力

Python面向对象:__iter__与__next__创建可迭代对象

Python面向对象:__iter__与__next__创建可迭代对象

在这里插入图片描述

一、开篇:让你的对象能被for循环

你每天都在用for循环遍历列表、字典、文件。但你是否想过——为什么你能for x in my_object?答案是迭代器协议:__iter__和__next__。

⌨️ 先看核心概念:

# 可迭代对象(Iterable):实现了__iter__,可以被for循环使用
# 迭代器(Iterator):实现了__iter__和__next__,逐个产出元素

# 内置的例子
my_list = [1, 2, 3]

# 1. my_list是可迭代对象——有__iter__
print(hasattr(my_list, '__iter__')) # True

# 2. 调用iter()获得迭代器
iterator = iter(my_list) # 等价于 my_list.__iter__()
print(type(iterator)) # <class 'list_iterator'>

# 3. 调用next()获取下一个元素
print(next(iterator)) # 1
print(next(iterator)) # 2
print(next(iterator)) # 3
# print(next(iterator)) # StopIteration —— 迭代结束

二、创建自定义迭代器

2.1 通过__iter__和__next__

class Countdown:
"""倒计时迭代器"""
def __init__(self, start):
self.start = start
self.current = start

def __iter__(self):
"""返回迭代器自身"""
return self

def __next__(self):
"""返回下一个元素"""
if self.current <= 0:
raise StopIteration # 迭代结束的信号
value = self.current
self.current -= 1
return value

# 使用
for n in Countdown(5):
print(n, end=" ") # 5 4 3 2 1
print()
print(list(Countdown(3))) # [3, 2, 1]

# ⚠️ 上述实现的限制:只能迭代一次
cd = Countdown(3)
print(list(cd)) # [3, 2, 1]
print(list(cd)) # [] —— 第二次为空!current已经到0了

2.2 分离可迭代对象和迭代器

# ✅ 更好的设计:可迭代对象每次产生新的迭代器
# 这样可以多次迭代!

class CountdownIterable:
"""倒计时——可多次迭代"""
def __init__(self, start):
self.start = start

def __iter__(self):
"""每次调用返回一个新的迭代器"""
return CountdownIterator(self.start)

class CountdownIterator:
"""倒计时迭代器——不需要在外部直接使用"""
def __init__(self, start):
self.current = start

def __iter__(self):
return self

def __next__(self):
if self.current <= 0:
raise StopIteration
value = self.current
self.current -= 1
return value

# 现在可以多次迭代了
cd = CountdownIterable(3)
print(list(cd)) # [3, 2, 1]
print(list(cd)) # [3, 2, 1] —— 没问题!每次迭代都是新的迭代器

三、用生成器简化实现

3.1 __iter__返回生成器

# 使用yield可以极大简化__iter__的实现
# 不需要单独写__next__

class Fibonacci:
"""斐波那契数列——使用生成器的__iter__"""
def __init__(self, max_count):
self.max_count = max_count

def __iter__(self):
"""生成器函数——自动创建迭代器"""
a, b = 0, 1
for _ in range(self.max_count):
yield a
a, b = b, a + b

# 使用
fib = Fibonacci(10)
print(list(fib)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# 可以多次迭代
print(list(fib)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

# 💡 这是最Pythonic的做法!
# 用yield在__iter__中定义迭代逻辑
# 简洁、可读、正确(自动支持多次迭代)

3.2 实际应用:分页遍历

class Paginator:
"""分页器——自动按页遍历大数据集"""
def __init__(self, items, page_size=10):
self.items = items
self.page_size = page_size

def __iter__(self):
"""按页产出数据"""
for i in range(0, len(self.items), self.page_size):
page = self.items[i:i + self.page_size]
page_num = i // self.page_size + 1
total_pages = (len(self.items) + self.page_size 1) // self.page_size
yield {
"page": page_num,
"total_pages": total_pages,
"items": page,
"has_next": page_num < total_pages,
}

def __len__(self):
"""返回总页数"""
return (len(self.items) + self.page_size 1) // self.page_size

# 使用
data = list(range(1, 26)) # 25条数据
paginator = Paginator(data, page_size=10)

for page in paginator:
print(f"第{page['page']}/{page['total_pages']}页: {page['items']}")
# 第1/3页: [1, 2, …, 10]
# 第2/3页: [11, 12, …, 20]
# 第3/3页: [21, 22, 23, 24, 25]

四、实战案例

4.1 日期范围迭代器

from datetime import datetime, timedelta

class DateRange:
"""日期范围——可以for循环遍历每一天"""
def __init__(self, start_date, end_date):
self.start_date = start_date
self.end_date = end_date

def __iter__(self):
current = self.start_date
while current <= self.end_date:
yield current
current += timedelta(days=1)

def __contains__(self, date):
"""支持 in 操作符"""
return self.start_date <= date <= self.end_date

def __len__(self):
"""返回天数"""
return (self.end_date self.start_date).days + 1

# 使用
start = datetime(2024, 6, 1)
end = datetime(2024, 6, 10)
date_range = DateRange(start, end)

# for循环遍历
for d in date_range:
if d.weekday() >= 5: # 周末
print(f"{d.strftime('%Y-%m-%d')} 是周末")

# in操作
check_date = datetime(2024, 6, 5)
print(f"{check_date.date()} {'在' if check_date in date_range else '不在'}范围内")
# 2024-06-05 在范围内

# len
print(f"共{len(date_range)}天")

4.2 文件行迭代器(带行号)

class LineNumberedFile:
"""读取文件——自动添加行号"""
def __init__(self, filepath, encoding='utf-8'):
self.filepath = filepath
self.encoding = encoding

def __iter__(self):
"""逐行产出,每行带行号"""
with open(self.filepath, 'r', encoding=self.encoding) as f:
for line_num, line in enumerate(f, 1):
yield {
"number": line_num,
"content": line.rstrip('\\n'),
"length": len(line.rstrip('\\n')),
"is_empty": line.strip() == "",
}

# 使用
# reader = LineNumberedFile("data.txt")
# for line_info in reader:
# if not line_info["is_empty"]:
# print(f"第{line_info['number']}行({line_info['length']}字): "
# f"{line_info['content'][:50]}…")

五、总结

实现__iter__让你的对象可以被for循环使用——这是Python集合生态的"通行证"。

💡 核心要点:

  • __iter__返回迭代器——让对象可迭代
  • __next__产出下一个元素——迭代器耗尽抛StopIteration
  • 最简单的实现——在__iter__中用yield
  • 分离可迭代对象和迭代器——支持多次迭代
  • 实现后免费获得:for循环、list()、in操作(部分)、sum()等
  • ✅ 最佳实践:在__iter__中用生成器(yield)——代码简洁且自动支持多次迭代。

    赞(0)
    未经允许不得转载:171主机测评 » Python面向对象:__iter__与__next__创建可迭代对象
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址