1 绪论
- 迭代是Python最强大的功能之一,是访问集合元素的一种方式。
- 迭代器是一个可以记住遍历的位置的对象。
- 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
- 迭代器有两个基本的方法:iter() 和 next()。
2 自定义迭代器
2.1 判断对象是否可迭代
-
isinstance
是一种判断对象类型的函数,和type
的功能差不多,但是使用的方法不同,具体的使用方法详见Python isinstance() 函数 | 菜鸟教程 - 使用
isinstance(a, Iterable)
可以判断a
是否为可迭代,在使用Iterable
之前应该先导入:from collections import Iterable
,举个例子如下:
>>> a = [1,2,3]
>>> b = list
>>> c = {1: '1', 2: '2', 3: '3'}
>>> from collections import Iterable
>>> isinstance(a, Iterable)
True
>>> isinstance(b, Iterable)
False
>>> isinstance(c, Iterable)
True
2.2 基于重写__getitem__
方法的自定义迭代器
- 这种方式并不常见,建议使用2.3中的方法
-
__getitem__
即利用索引获取值,用该方法书写迭代器的好处就是可以通过索引(即a[1]
这种形式)获取迭代器的值 - 下面展示通过修改
__getitem__
方法手写python的range
方法
class MyRange:
"""创建一个自己的Range函数"""
def __init__(self, minn, maxn=None, step=1):
"""初始化MyRange"""
self.minn, self.maxn = (0, minn) if maxn is None else (minn, maxn)
self.step = step
diff = self.maxn - self.minn # 差
self.cnt = diff // step + (0 if diff % step == 0 else 1)
# "//"的意思是向下取整除
# 整句话的意思是向上取整除,获得输出对象的个数
self.cur = self.minn
def __getitem__(self, item):
"""重写方法"""
if item < self.cnt:
return self.minn + (item + self.cnt) % self.cnt * self.step
raise IndexError('索引{}超出了范围{}'.format(item, self.maxn))
- 运行之后
>>> for i in MyRange(2, 10, 3):
... print(i, end=' ' if i < 8 else '\n')
...
2 5 8
>>> range(10)[-3]
7
>>> MyRange(10)[-3]
7
2.3 基于重写__iter__
方法和__next__
方法的自定义迭代器
- 【注意】当类中没有
__getitem__
方法时,上述的两种方法必须显式的定义在类中,才能使定义的类被识别为迭代对象。如果没有__getitem__
方法,这个类对象不能通过索引获取元素。 -
__iter__
方法可以将需要的类返回作为迭代器类型 - 逐次
__next__
方法指定产生的返回结果 - 我们将上面的
MyRange
类用__iter__
和__next__
实现:
在这里插入代码片
3 其他常用迭代器
3.1 iter
和next
- 在上文中介绍了如何使用
__iter__
方法和__next__
方法自定义一个迭代器 -
iter
方法即调用对象的__iter__
方法,生成一个迭代器 -
next
方法即调用对象的__next__
方法,将迭代器进行一次迭代,并返回对象的__next__
方法的返回值。如果对象中没有__next__
方法,则next
会调用对象的__getitem__
方法,并将参数item
的初始值设为0,返回__getitem__
方法的返回值
class MyRange2:
"""创建一个自己的Range函数"""
def __init__(self, minn, maxn=None, step=1):
"""初始化MyRange"""
self.minn, self.maxn = (0, minn) if maxn is None else (minn, maxn)
self.step = step
diff = self.maxn - self.minn # 差
self.cnt = diff // step + (0 if diff % step == 0 else 1)
# "//"的意思是向下取整除
# 整句话的意思是向上取整除,获得输出对象的个数
self.cur = self.minn
def __iter__(self):
"""创建迭代器"""
return self
def __next__(self):
"""返回下一元素"""
if self.cur < self.maxn:
self.cur += self.step
return self.cur - self.step
else:
raise StopIteration
- 运行结果:
for i in MyRange2(2, 10, 3):
print(i, end=' ' if i < 8 else '\n')
2 5 8
print(range(10)[-3])
7
print(MyRange2(10)[-3])
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'MyRange2' object is not subscriptable
3.2 zip
同时去除多个对象中索引相同的元素
-
zip()
函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。 - 我们可以使用 list() 转换来输出列表。
- 如果各个迭代器的元素个数不一致,则返回列表长度与最短的对象相同
- 利用 * 号操作符,可以将元组解压为列表,即
zip(*)
可以理解为解压
>>>a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 返回一个对象
>>> zipped
<zip object at 0x103abc288>
>>> list(zipped) # list() 转换为列表
[(1, 4), (2, 5), (3, 6)]
>>> list(zip(a,c)) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> a1, a2 = zip(*zip(a,b)) # 与 zip 相反,zip(*) 可理解为解压,返回二维矩阵式
>>> list(a1)
[1, 2, 3]
>>> list(a2)
[4, 5, 6]
>>>