迭代和循环的区别
python 的 for in
语法是比较特殊的, 不同于其他编程语言的 for 语法, python 中的 for in 语法不能用于数字, 只能用于可迭代对象, 那么如何判断一个对象是否是可迭代对象呢?
py
# 迭代: 每次调用迭代器方法, 直接获取值
items = [1, 3, 5, 7, 9]
for item in items:
print(f"item value is: {item}")
# 循环:
# 1. 首先获取列表的长度
# 2. 列表的索引从 0 开始, 每次循环让 i += 1
# 那么此时 i 每次就是列表的每个元素的索引
# 3. 所以在循环中, 取值需要通过索引 items[i]
items = [1, 3, 5, 7, 9]
i = 0
items_length = len(items)
while i < items_length:
print(f"item value is: {items[i]}")
i += 1
可迭代对象与迭代器对象
可迭代对象
简而言之: 实现了可迭代协议的对象
, 就可以称为 可迭代对象
或者说: 只要是能被 for in
语法迭代的对象, 都可以称之为可迭代对象
比如: 字符串,列表,元组,集合等等
迭代器对象
不仅要实现可迭代协议, 还必须要在 __iter__
方法总返回对象本身(也就是返回 self)
那么也就是说, 迭代器对象一定是可迭代对象, 但是可迭代对象不一定是迭代器对象
判断是否是可迭代对象
py
from collections.abc import Iterable
# False
print(f"数字可迭代吗? { isinstance(123, Iterable) }")
# True
print(f"字符串可迭代吗? { isinstance('123', Iterable) }")
# True
# list/tuple/set/dict
print(f"list 可迭代吗? { isinstance([1,2,3], Iterable) }")
print(f"tuple 可迭代吗? { isinstance((1,2,3), Iterable) }")
print(f"set 可迭代吗? { isinstance({1,2,3}, Iterable) }")
print(f"dict 可迭代吗? { isinstance({"a":1,"b":2}, Iterable) }")
判断是否迭代器对象
py
from collections.abc import Iterable, Iterator
print(f"列表是迭代器对象吗?{isinstance([1,2,3], Iterator)}") # False
print(f"元组是迭代器对象吗?{isinstance((1,2,3), Iterator)}") # False
print(f"集合是迭代器对象吗?{isinstance({1,2,3}, Iterator)}") # False
stack = Stack() # Stack 是下面可迭代协议的代码
print(f"stack 是迭代器对象吗?{isinstance(stack, Iterator)}")
可迭代协议
在 Python 中, 可迭代协议定义了一种标准的方式来遍历集合中的元素
要实现迭代器协议, 一个对象必须带有以下两个方法 __iter__
和 __next__
__iter__
方法会返回一个带有__next__
的对象__iter__
方法如果返回对象本身self
, 那么就可以称之为迭代器对象
, 否则就只能叫可迭代对象
__next__
方法会返回下一个访问到的元素
py
################################
# 手动实现可迭代协议
################################
from collections.abc import Iterable, Iterator
class Stack:
def __init__(self):
self.items = []
self.index = 0 # 用于迭代器的位置追踪
# 入栈
def push(self, item):
self.items.append(item)
# 出栈
def pop(self):
if not self.is_empty():
return self.items.pop()
else:
raise IndexError("pop from an empty stack")
# 清栈
def clear(self):
self.items = []
# 判断栈是否为空
def is_empty(self):
return len(self.items) == 0
# 返回栈的长度
def size(self):
return len(self.items)
# 实现迭代器协议(就是返回一个带有: __iter__ 和 __next__ 方法的对象)
def __iter__(self):
# 从栈顶开始迭代
self.index = len(self.items) - 1
return self
# 迭代一般是将一组数据, 看做有序的数据集合
# 所有 __next__ 用于访问数据集合中的下一个元素
def __next__(self):
if self.index < 0:
# 如果索引小于0, 说明栈中元素全部被
# 访问(迭代)过了, 说明迭代可以结束了
raise StopIteration
item = self.items[self.index]
self.index -= 1 # 移动到下一个位置
return item
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
# 迭代输出栈中的所有元素
# 因为 stack 实现了可迭代协议, 那么 stack 就是一个可迭代对象
# 所以可以这样迭代:
print(isinstance(stack, Iterable)) # True
for item in stack:
print(f"item is: {item}")
print("-" * 30)
# 因为 __iter__ 方法返回了一个可迭代对象
# 所以可以这样遍历:
print(stack == stack.__iter__()) # false
for item in stack.__iter__():
print(f"item is: {item}")
print("-" * 30)
print(f"stack 是否是可迭代对象 {isinstance(stack, Iterable)}") # True
print(f"stack 是否是迭代器对象 {isinstance(stack, Iterator)}") # False
py
from collections.abc import Iterable, Iterator
class Stack2:
def __init__(self):
self.items = []
# 入栈
def push(self, item):
self.items.append(item)
# 出栈
def pop(self):
if not self.is_empty():
return self.items.pop()
else:
raise IndexError("pop from an empty stack")
# 清栈
def clear(self):
self.items = []
# 判断栈是否为空
def is_empty(self):
return len(self.items) == 0
# 返回栈的长度
def size(self):
return len(self.items)
# 返回一个带有 __iter__ 和 __next__ 方法的对象
# 但是这个对象并不是当前实例 self
# 所以这样的对象, 只能叫 可迭代对象, 不能叫 迭代器对象
def __iter__(self):
return StackIterator(self.items)
class StackIterator:
def __init__(self, data):
self.data = data
self.index = 0
def __iter__(self):
return self
def __next__(self):
if self.index >= len(self.data):
raise StopIteration
value = self.data[self.index]
self.index += 1
return value
stack = Stack2()
stack.push(1)
stack.push(2)
stack.push(3)
# 因为 stack 实现了可迭代协议, 那么 stack 就是一个可迭代对象
# 所以可以这样迭代:
print(isinstance(stack, Iterable)) # True
for item in stack:
print(f"item is: {item}")
print("-" * 30)
# 因为 __iter__ 方法返回了一个可迭代对象
# 所以可以这样迭代:
print(stack == stack.__iter__()) # True
for item in stack.__iter__():
print(f"item is: {item}")
print("-" * 30)
print(f"stack 是否是可迭代对象 {isinstance(stack, Iterable)}") # True
print(f"stack 是否是迭代器对象 {isinstance(stack, Iterator)}") # False
对比现实, 理解概念
可迭代协议
代表一种能力, 实现可迭代协议
的都可以称之为可迭代对象
可迭代对象
只有满足某种特定要求后才能称之为迭代器对象
- 总结来说: 迭代器对象一定是可迭代对象, 可迭代对象不一定是可迭代对象
- 我可以跑步, 跑步运动员也可以跑步, 我们都实现了
可跑步协议
, 都可以称为可跑步的人
- 能参加奥运会跑步比赛的人才能称之为
跑步运动员
, 而不满足要求的人只能称为可跑步的人
跑步运动员
一定是可跑步的人
, 但是可跑步的人
不一定是跑步运动员
iter 函数
- iter函数 是一个可以将
可迭代对象
转换成迭代器对象
的内置函数
py
from collections.abc import Iterable, Iterator
items = [1, 2, 3]
items_iter = iter(items)
print(f"是可迭代对象吗?{isinstance(items, Iterable)}") # True
print(f"是迭代器对象吗?{isinstance(items, Iterator)}") # False
print("-" * 30)
print(f"是可迭代对象吗?{isinstance(items_iter, Iterable)}") # True
print(f"是迭代器对象吗?{isinstance(items_iter, Iterator)}") # True
next 函数
不通过 for in 语法自动迭代, 手动获取迭代器函数的值
py
it = iter([1, 2, 3])
print(next(it)) # 1
print(next(it)) # 2
print(next(it)) # 3
# 如果超过就会抛出异常
print(next(it))
print(next(it))
for in 语法的原理
py
# 根据以下特性, 手动实现一个 for in
# 1. for in 只能迭代可迭代对象
# 2. iter 可以将 可迭代对象 转换成 迭代器对象
# 3. next 可以手动迭代, 超出就抛出异常
def for_in(iterable_object, handler):
try:
it = iter(iterable_object)
value = next(it)
while(value):
handler(value)
value = next(it) # 一直向后迭代
except Exception as e:
print("迭代结束了", e)
for_in([1, 2, 3], print)
生成器函数
生成器函数其实应该叫 生成可迭代对象函数
, 因为他会自动返回一个可迭代对象
py
from collections.abc import Iterable, Iterator
def get_nums(start: int, end: int):
i = start
while i <= end:
# 用 yield 关键字向外返回结果
yield i
i += 1
return i
# 此时的 nums 就是一个可迭代对象
nums = get_nums(1, 9)
print(f"is iterable:{ isinstance(nums, Iterable) }") # True
print(f"is iterator:{ isinstance(nums, Iterator) }") # True
# 此时就直接可以使用 for in 遍历
for num in nums:
print(f"num value is:{num}")