1 迭代器与可以迭代对象
python中可以循环的字符串、容器都是可迭代对象。了解迭代必须要明白两个内置函数iter()和next()
1.1 iter()和next() 内置函数
iter() 返回一个迭代器对象;
print(iter([1,2,3]))
print(iter("hello"))
print(iter(123))
# 输出
<list_iterator object at 0x10a979d60>
<str_iterator object at 0x10a979d60>
Traceback (most recent call last):
TypeError: 'int' object is not iterable
迭代器(interator): 迭代器帮助迭代的对象,特点是可以通过next(),不断获取迭代器中下一个成员,如果没有更多的值返回抛出StopInteration异常。
l = [1, 2, 4, "x"]
i = iter(l)
print(i) # <list_iterator object at 0x10324ae80>
print(next(i)) # 1
print(next(i)) # 2
i_1 = iter(i)
print(i is i_1) # True
python循环都是先通过iter(obj), 获得迭代器,然后通过next() 获取迭代器中的值。
2、自定义迭代器
自定义迭代器需要实现
1 __iter__: 调用的时候返回迭代器本身。 2 __next__: 调用next()的时候触发,通过returen返回结果。如果没有更多的内容就抛出异常。
class Range7:
"""生成某个范围内被7整除或包含7的整数"""
def __init__(self, start, end):
self.start = start
self.end = end
self.current = start
def __iter__(self):
return self
def __next__(self):
while True:
if self.current >= self.end:
raise StopIteration
if self.num_is_valid(self.current):
ret = self.current
self.current += 1
return ret
self.current += 1
def num_is_valid(self, num):
if num == 0:
return False
return num % 7 == 0 or '7' in str(num)
测试代码:
r = Range7(0, 20)
print(r)
for num in r:
print(num)
# 第二次遍历拿不到任何数据
for num in r:
print(num)
第二次不反回结果是因为self.current 没有被重置,是因为所有的迭代器,都是这样设计的。
3、区分迭代器与可迭代对象
迭代器有时候与可迭代对象非常相似,但是其含义不相同:
迭代器:迭代器是可迭代对象的一种,在迭代其他对象时,迭代器作为一种介质或者工具对象的存在,迭代的list或者字符串的时候返回list_interator或str_interator等。每次迭代器迭代一个可迭代对象都要保证迭代的顺序从最顶开始。
迭代器必须同时实现两个魔法方法:1 __iter__ 、 2 __next__:
可迭代对象:可迭代对象,判断一个对象是不是可迭代对象,通过iter(obj), 看返回结果是不是一个迭代器,因为可迭代对象只需要实现 __iter__ 方法, 不一定要实现 __next__ 方法。
为了让Range7对象每次迭代都返回结果:需要自定义可迭代对象Rang7和 自定义迭代器Range7Interator
class Range7:
"""生成某个范围内被7整除或包含7的整数"""
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return Range7Interator(self)
class Range7Interator:
def __init__(self, range7_obj):
self.range7_obj = range7_obj
self.current = range7_obj.start
def __iter__(self):
return self
def __next__(self):
while True:
if self.current >= self.range7_obj.end:
raise StopIteration
if self.num_is_valid(self.current):
ret = self.current
self.current += 1
return ret
self.current += 1
def num_is_valid(self, num):
if num == 0:
return False
return num % 7 == 0 or '7' in str(num)
测试代码:
r = Range7(0, 20)
print(r)
for num in r:
print(num)
# 第二次遍历:仍然可以拿到数据
for num in r:
print(num)
总结一下迭代器与可迭代对象的区别:
1、可迭代对象不一定是迭代器,迭代器一定是可迭代对象。
2、可迭代对象使用iter()会返回迭代器,迭代器调用iter() 返回迭代器自身
3、每个迭代器的被迭代过程是一次性的,可迭代对象则不一定。
4、可迭代对象只需要实现_ _ iter_ _ 方法,而迭代器需要额外实现 _ _ next _ _ 方法。
2 生成器是迭代器
2.1生成器是一种简化了的迭代器的实现
可以使用生成器来降低传统迭代器的编码成本,不需要实现两个方法来实现迭代器。
class Range7:
"""生成某个范围内被7整除或包含7的整数"""
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return self.range_generator(self.start, self.end)
def range_generator(self, start, end):
num = start
while num < end:
if num != 0 and (num % 7 == 0 or '7' in str(num)):
yield num
num += 1
测试代码:
range_ = Range7(0, 20)
for x in range_:
print(x)
for x in range_:
print(x)
同构定义一个生成器函数,同样实现了Range7是一个可迭代对象。
2.2 生成器函数修饰可迭代对象
生成器函数 修饰可迭代对象,类似于filter, 可以过滤可迭代对象
def sum_only(numbers):
"""对numbers的偶数求和
"""
result = 0
for num in numbers:
if num % 2 == 0:
result += num
return result
定义生成器修饰numbers类似于过滤:
def even_only(numbers):
for num in numbers:
if num % 2 == 0:
yield num
def sum_only_v1(numbers):
"""对numbers的偶数求和
"""
result = 0
for num in even_only(numbers):
result += num
return result
生成器优化写法:
def sum_only_v2(numbers):
"""对numbers的偶数求和
"""
result = 0
for num in [num for num in numbers if num % 2 == 0]:
result += num
return result