这两天准备用Pytorch复现一些论文,所以也顺便熟悉了一下Pytorch在Python部分的实现,发现Pytorch用到了很多Python迭代器方面的语法。
所以准备先介绍一下Python中迭代器和生成器的使用,之后再介绍Pytorch的一些内部实现。
目录
1.前言
2.预备知识
2.1 什么是迭代器对象?
2.2 什么是可迭代对象?
3. for循环遍历可迭代对象的逻辑
4. 分离可迭代对象和迭代器对象
5. 如何显式地从可迭代对象中得到迭代器对象(iter函数)?
6. 如何显式地对迭代器对象进行迭代(next函数)?
7. 生成器:利用函数创建可迭代的迭代器对象
8. 如何对迭代器进行多次遍历?
9. 参考资料
1.前言
迭代器的好处不用多说,就是节省内存。这里主要介绍Python中迭代器和生成器的使用方法。
2.预备知识
2.1 什么是迭代器对象?
迭代器对象就是拥有__next__方法的对象。
通常我们不会单独使用迭代器对象。
2.2 什么是可迭代对象?
可迭代对象就是拥有了__iter__方法的对象。
注意一个对象可以同时是迭代器对象和可迭代对象(即既实现了__iter__方法,又实现了__next__方法)下文中我将称这类对象为可迭代的迭代器对象。
3. for循环遍历可迭代对象的逻辑
这里我想先介绍一下迭代器最常见的一个使用场景:for循环。
然后再剖析里面的一些细节。
class MyNumbers:
def __init__(self):
self.a = 1
def __iter__(self):
return self
def __next__(self):
x = self.a
self.a += 1
return x
mn = MyNumbers():
for i in mn:
print(i)
"""
输出结果:
1
2
3
4
5
6
∞
"""
这里我实现了一个MyNumbers类,这个类实现了__iter__和__next__方法。其中__iter__方法返回的是自己。这也是一般的可迭代对象的方式,即既实现__iter__,又实现__next__。
for循环的内部逻辑其实也不复杂,首先for循环会调用可迭代对象的__iter__方法,得到一个迭代器对象,之后再循环调用这个迭代器对象的__next__方法。
按照这个逻辑,我们可以分别在两个类中实现__iter__和__next__方法。
4. 分离可迭代对象和迭代器对象
大多数样例代码会在同一个类中实现__iter__和__next__方法,这里我提供一个分开实现的示例。
即让MyNumbers类实现__next__方法;Iterable类实现__iter__方法。这样做也是可以的。
class MyNumbers:
def __init__(self):
self.a = 1
def __next__(self):
x = self.a
self.a += 1
return x
class Iterable:
def __init__(self,Iterator):
self.Iterator = Iterator
def __iter__(self):
return self.Iterator
Iterator = MyNumbers()
Iterable_object = Iterable(Iterator)
for i in Iterable_object:
print(i)
5. 如何显式地从可迭代对象中得到迭代器对象(iter函数)?
一般来说我们都是在for循环中隐式地调用可迭代对象的__iter__方法来获得迭代器对象,但是也可以显式地获得迭代器。
iter函数或者直接调用__iter__方法都是可以的。
class MyNumbers:
def __init__(self):
self.a = 1
def __iter__(self):
return self
def __next__(self):
x = self.a
self.a += 1
return x
mn = MyNumbers()
print(type(iter(mn)))
print(type(mn.__iter__()))
6. 如何显式地对迭代器对象进行迭代(next函数)?
同样,for循环中对__next__方法的调用也是隐式的,我们可以用next函数,或者__next__方法直接对迭代器对象进行迭代。
class MyNumbers:
def __init__(self):
self.a = 1
def __iter__(self):
return self
def __next__(self):
x = self.a
self.a += 1
return x
mn = MyNumbers()
print(next(mn))
print(mn.__next__())
print(next(mn))
print(mn.__next__())
"""
输出:
1
2
3
4
"""
7. 生成器:利用函数创建可迭代的迭代器对象
上面我们已经了解了,通过在类中实现__iter__和__next__方法,可以创建可迭代对象,迭代器对象或者可迭代的迭代器对象。
下面介绍另一种通过函数来得到可迭代的迭代器对象的方法,
而这种通过函数同时实现了__iter__和__next__方法的对象也可以被称为生成器。
注意生成器和可迭代的迭代器对象是存在区别的。
这个例子中,我创建了一个MyNumbers类的生成器版。
可以看到通过调用具有yield关键字的函数,可以得到一个generator类的实例。
def fun_mynumbers(upper_bound):
x=1
while True:
if(x > upper_bound):
return
yield x
x+=1
print(type(fun_mynumbers))
mn_generator = fun_mynumbers(10)
print(type(mn_generator))
print(type(mn_generator.__iter__))
print(type(mn_generator.__next__))
"""
输出结果:
"""
接下来遍历这个生成器,可以发现在用for循环遍历了生成器一遍之后,再次对生成器调用__next__方法就会报StopIteration错。这就是生成器和一般可迭代的迭代器对象的不同。
for i in mn_generator:
print(i)
mn_generator.__next__()
"""
输出结果:
1
2
3
4
5
6
7
8
9
10
StopIteration Traceback (most recent call last)
/tmp/ipykernel_38/2244425320.py in
4 for i in mn_generator:
5 print(i)
----> 6 mn_generator.__next__()
StopIteration:
"""
8. 如何对迭代器进行多次遍历?
所以之后我会展示一个可以多次遍历的可迭代的迭代器对象。
可以看到这里之所以可以多次遍历,就是因为在实现__next__方法的时候会先判断当前要输出的值是否超过上界,假如没超过,那么正常输出,假设超过了,那么将当前的值重置,并且结束这次遍历。
class MyNumbers:
def __init__(self,upper_bound):
self.upper_bound = upper_bound
self.a = 1
def __iter__(self):
return self
def __next__(self):
if(self.a < self.upper_bound):
x = self.a
self.a += 1
return x
else:
self.a = 1
raise StopIteration
mn = MyNumbers(5)
for i in mn:
print(i)
for i in mn:
print(i)
"""
输出结果:
1
2
3
4
1
2
3
4
"""
9. 参考资料
Python3 迭代器与生成器 | 菜鸟教程
python的迭代器为什么一定要实现__iter__方法? - 知乎



