首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

搬砖的也能玩Python-进阶篇1

搬砖的也能玩Python——进阶篇

1-迭代器

回顾

在讲循环语句的基础篇5中提到,for循环会访问一个可迭代对象,并且遍历可迭代对象中的每一个元素,那么具体的原理是什么呢?我们的进阶篇第一篇文章,就带大家来了解一下“迭代器”的概念。

一、迭代器的相关概念

迭代器

迭代器(iterator)就是一个实现了迭代器协议的对象,这个对象包含下面两个方法:

__next__():获取容器中的下一个对象。

__iter__():获取迭代器对象。

每次使用__next__()方法便会得到下一个元素,当所有的元素都获取完之后,便会引发一个StopIteration异常。我们的for循环就是先调用__next__()方法来遍历每一个元素,当for循环捕获StopIteration异常之后,循环就自动停止了。

在Python中,可以用iter()方法来创建一个迭代器,我们来看一个简单的例子:

在上图的例子中,我们用iter()方法,将一个列表变成了一个装饰器,并赋值给了变量i,针对i,我们每次使用next()方法,便会依次得到迭代器中的元素。当我们3个元素都的出来之后,又一次执行了next()方法,我们来看看结果。

结果会报出StopIteration错误。

迭代器对象

在Python中,对于实现了__next__()方法的对象,同时__iter__()方法返回对象本身,我们称之为迭代器对象。

可迭代对象

在Python中,对于实现了__iter__()方法的对象,我们称之为可迭代对象,可迭代对象中的__iter__()方法,返回了一个迭代器对象。

接下来,我们一步一步的了解迭代器对象和可迭代对象。

二、迭代器对象

想要自定义一个迭代器,只需要编写一个具有__next__()方法的类,并且__iter__()方法返回迭代器实例即可。我们来看下面这个例子:

在上图的例子中,我们定义了一个列表迭代器的类ListIterator,并且实现了__iter__()方法和__next__()方法,__iter__()方法返回了迭代器本身(self),__next__()方法我们先判断当前的索引是否超过了列表的长度,如果没有超过,就把列表当前索引的值输出,如果超过列表长度,就抛出StopIteration异常,这样就会使for循环结束。我们来看一下如何使用这个迭代器:

我们定义了一个实例nba,传进去了一个列表,在使用for循环的时候,我们写了一个pass语句,就是单纯把nba这个实例遍历一遍,并不执行任何的操作,我们来看一下结果是怎么样的。

从结果中我们看出,执行for循环时,先寻找并执行__iter__()方法,由于存在__iter__()方法,所以我们的nba实例是可迭代的,接下来for循环会自动调用__next__()方法,执行我们__next__()方法下的内容,直到最后一次执行时,捕获到了StopIteration异常,for循环结束。

其实我们还可以用while循环,来模拟一下for循环的实现过程:

从上图中看到,我们把while循环写成一个无限循环,每次都去调用__next__(),知道捕获到StopI异常,通过break结束循环,这样也能实现for循环的效果。我们来看一下结果。

跟for循环的结果相比,我们只是没有执行__iter__()方法,for循环会先来寻找__iter__()方法,来判断是否是可迭代的,只有可迭代的,for循环才会继续执行。

思考

针对上面的nba实例,如果我们连续使用两次for循环遍历,会有什么样的结果呢?

解析

我们来看一下下图的执行情况及结果。

从上图中的结果看出,执行第一遍for循环时,结果是正常的,执行第二时,直接就捕获了StopIteration异常,也就是没有找到nba中的元素。这是为什么呢?

原因就是,同一个迭代器对象不能多次迭代,迭代器只能向后移动,不能回到开始。

有什么办法可以解决呢?这就需要可迭代对象的帮忙啦!

三、可迭代对象

我们来具体看一下如何解决上面那个重复for循环的问题:

从上图中看到,我们在原有的ListIterator的基础上,又定义了一个新的类ListIterable,这个类除了初始化一个列表之外,只有__iter__()方法,并没有__next__()方法,并且__iter__()返回的是我们ListIterator的一个实例,也就是返回了一个迭代器对象。那么我们的ListIterable就是一个可迭代对象。

这样我们实例化一个可迭代对象,连续使用for循环,来看一下结果。

从上图中,我们看到这两次for循环都能遍历出nba中的元素,实现了多次迭代。

解析

其实这并不是多次迭代,只是在我们每次使用for循环时,都是找到的ListIterable中的__iter__()方法,而__iter__()方法会返回一个迭代器对象。所以这相当于我们每次都生成了一个新的迭代器对象ListIterator对象,这两个for循环对应的ListIterator并不是同一个,所以才能都遍历出来。

四、关于迭代器中的下一个元素

在迭代器中,我们要注意一个问题,就是迭代器只是记录了当前达到序列中的第几个元素,当我们的序列发生动态变化时,我们的结果也是实时更新的,来看下面这个例子。

从上图中看到,我们先定义了一个列表,通过for循环来遍历这个列表,输出当前的值之后,删除当前的值。我们来看一下结果。

为什么会这样呢?我们来逐次循环看一下:

第一次循环开始时,我们的列表为["火箭", "马刺", "独行侠", "灰熊", "鹈鹕"],此时我们索引为0,得到的值也就是"火箭",紧接着把"火箭"移除,列表发生动态变化,变成了["马刺", "独行侠", "灰熊", "鹈鹕"],此时的索引依旧为0,这个时候索引为0的值已经变成了"马刺",但此时第一次循环已经结束了,所以没有任何结果输出;

第二次循环开始时,列表为["马刺", "独行侠", "灰熊", "鹈鹕"],此时我们索引变成了1,在这个发生了变化的列表中,索引为1的值是"独行侠",所以我们得到的值就是"独行侠",紧接着把"独行侠"移除,列表发生动态变化,变成了["马刺", "灰熊", "鹈鹕"],索引为1的值变成了"灰熊",此时第二次循环也结束了;

第三次循环开始时,列表为["马刺", "灰熊", "鹈鹕"],此时索引变成了2,在这个新的列表中,索引为2的值是"鹈鹕",所以我们得到的值就是"鹈鹕",紧接着把"鹈鹕"移除,列表发生动态变化,变成了["马刺", "灰熊"],第三次循环结束;

第四次循环,索引变为3,超过了列表的长度,会捕获到StopIteration异常,循环结束。最后输出此时的列表,得到我们上图中的结果。

To Be Continued

第一次尝试写更复杂、更难懂的内容,希望通过文章中的实例大家可以理解迭代器,下次进阶篇我们将了解一个特殊的迭代器——生成器,大家敬请期待。

探测八个蛋∣跳出手工测试的井

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180210G14CI600?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券