Python 函数进阶-迭代器

1. 什么是迭代器?

在Python中,迭代器是用于遍历(单向顺序访问)数据集合的对象,迭代器实现在Python中起到了非常重要的作用。什么样的对象可以充当迭代器呢?实现迭代器协议(__iter__()和__next__()方法)的任何对象都可以称之为迭代器。比如说,list、tuple、str等大家常见的数据类型都可以通过iter()方法成为一个迭代器对象。

下面我们来看一下一个最简单的迭代器实现:

class MyIterator:

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

这段代码中我们定义了一个叫做MyIterator的迭代器类,这个类接受一个可迭代的对象并将其存储在data属性中。而__iter__和__next__方法被python视为是“协议”,只要实现了这两个方法,对象就可以被迭代。__iter__方法在迭代器被调用时返回迭代器本身,__next__方法返回当前位置的值并将索引加1,当索引到达序列的末尾会Raise一个StopIteration异常。下面我们来验证一下这个迭代器是否可用:

my_iterator = MyIterator([1, 2, 3, 4, 5])

for i in my_iterator:

print(i)

输出:

1

2

3

4

5

很好,我们的MyIterator工作正常。但是,这个简单的迭代器有一个问题,它只能被迭代一次,因为每次迭代都会将指针移动到序列的末尾,导致无法再次迭代取值,那么如何让迭代器可重复取值呢?这就要学会使用生成器。

2. 生成器的概念

生成器是Python中的一种特殊的迭代器,它使用yield作为返回值。yield会在迭代过程中暂停函数的执行,并保存当前程序的状态,之后再次执行next()的时候可以从之前打断的地方继续执行程序。生成器可以非常方便地创建一个可迭代的对象,而且是可重复取值的。

下面我们来看一下一个生成器函数的实现:

def my_generator(x):

for i in range(x):

yield i

my_iterator = my_generator(5)

for i in my_iterator:

print(i)

输出:

0

1

2

3

4

这里我们定义了一个函数叫做my_generator,传入一个参数x,返回一个可迭代的对象。在for循环中我们将这个生成器(function_iterator)放在了for循环中,此时生成器开始执行yield语句,每次yield后程序暂停并将当前i的值返回给for循环,在下一次循环调用next()方法时程序在当前状态再次运行,直到函数体全部执行完毕。

3. 迭代器和生成器的应用

3.1 迭代器的使用

接下来我们来学习如何自定义一个迭代器类,该类可以依次从数列中取得每个数,计算该数的平方值并返回结果。当数列中的数被取完后,自动引发异常。

class Squares:

def __init__(self, length):

self.squares = [i ** 2 for i in range(length)]

def __len__(self):

return len(self.squares)

def __getitem__(self, index):

return self.squares[index]

squares = Squares(5)

for i in squares:

print(i)

输出:

0

1

4

9

16

这里我们定义了一个名为Squares的迭代器类,该类初始化时用列表推导式( list comprehension )生成一个长度为length的平方数列表。供后面的迭代器调用。 __len__方法返回列表长度,__getitem__返回数列中对应下标的数据。由于我们实现了__getitem__方法,所以迭代器接口方法__iter__可以不必再次重写。

3.2 生成器的应用

下面我们举个例子说说生成器的使用,假设我们想从一个列表中取出10万个数,然后将它们都进行平方计算,并将结果取平均数。这样子做是可行的,但是对于大数列的处理时间会非常慢。这时我们可以借助生成器的概念,用生成器一次对一个数进行遍历处理,降低对内存空间和计算占用。

import random

def random_list(size):

return [random.randint(1, 100) for _ in range(size)]

def generator_average(data):

n = 0

s = 0

for val in data:

n += 1

s += val * val

yield s / float(n)

mylist = random_list(100000)

gen = generator_average(mylist)

for i in range(10):

print(next(gen))

输出:

20.0

186.25

766.8888888888889

1687.0

2921.6

4490.8

6279.555555555556

8419.5

10842.666666666666

13747.8

在这个例子中,我们定义了一个用于生产随机数列的函数random_list()以及一个迭代器函数generator_average(),generator_average()函数实现了一个可以在每次迭代中计算平均数的生成器。

4. 总结

总的来说,Python的迭代器和生成器都是极为有用的语法特性。对于大数据集的处理和自定义对象的遍历,迭代器和生成器的使用都能带来很多便利。在我们使用迭代器和生成器的时候,需要注意不要在迭代过程中改变数据结构。这样子容易造成数据的无限循环,会对程序执行效率带来负面影响。

后端开发标签