前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >轻松搞懂Python中Iterable与Iterator

轻松搞懂Python中Iterable与Iterator

作者头像
触摸壹缕阳光
发布2020-04-12 18:58:07
2K0
发布2020-04-12 18:58:07
举报
文章被收录于专栏:AI机器学习与深度学习算法

前言

本文主要介绍Python中的Iterable与Iterator,其中Iterable为可迭代对象,Iterator为迭代器对象。

目录:

  1. Iterable与Iterator的介绍;
  2. Iterable与Iterator的关系;
  3. 如何判断Iterable和Iterator;
  4. 如何自定义一个迭代器;
  5. 关于迭代问题;

a

Iterable与Iterator的介绍

  • iterable:具体应该叫做可迭代对象。他的特点其实就是我的序列的大小长度已经确定了(list,tuple,dict,string等)。他遵循可迭代的协议。

可迭代协议: 含__iter__()方法。且可迭代对象中的__iter__()方法返回的是一个对应的迭代器。(如list对应的迭代器就是list_iterator)

  • iterator:具体应该叫做迭代器的对象。他的特点就是他不知道要执行多少次,所以可以理解不知道有多少个元素,每调用一次__next__()方法,就会往下走一步,当然是用__next__()方法只能往下走,不会回退。是惰性的。这样我可以存很大很大的数据,即使是整个自然数,也可以很轻松的用迭代器来表示出来。他满足的是迭代器协议。

迭代器协议:

  • 含__iter__()方法。且方法返回的Iterator对象本身
  • 含__next__()方法,每当__next__()方法被调用,返回下一个值,直到没有值可以访问,这个时候会抛出stopinteration的异常。

b

Iterable与Iterator的关系

我们从上面的介绍可以看出。通俗的讲就是类中如果满足可迭代的协议也就是有__iter__()的时候就可以成为可迭代对象。同理如果一个类中有__iter__()和__next__()方法的时候也就可以称之为迭代器。那他们两个到底什么关系呢?

代码语言:javascript
复制
>>> from collections import Iterator, Iterable
>>> help(Iterator)
Help on class Iterator:

class Iterator(Iterable)
 |  Method resolution order:
 |      Iterator
 |      Iterable
 |      builtins.object   
 |**注解:从这里可以看出Iterable继承自object, Iterator继承自Iterable。
 |  Methods defined here:
 |
 |  __iter__(self)
 |
 |  __next__(self)
 |      Return the next item from the iterator. When exhausted, raise StopIteration
......
>>> help(Iterable)
Help on class Iterable:

class Iterable(builtins.object)
 |  Methods defined here:
 |
 |  __iter__(self)
......

从上面的代码我们很清楚的看出Iterator继承iterable。这样我们就很清楚的看到了他们之间的关系了。

那我们说能不能把iterable转换Iterator呢?当然可以。可以通过iter()函数进行转换。其实说白了执行iter()方法就是去调用类中的__iter__()方法。其实前面也说了对于iterable如果执行了__iter__()方法他返回的是对应的itertor对象。如果Iterator调用__iter__()方法他返回的就是他自己(也就是一个迭代器)。

iter(iterable)-->iterator iter(iterator)-->iterator

那我们看看下面这段代码:

代码语言:javascript
复制
list = [1,2,3,4]
list_iterator = iter(list)
list.__next__()
Traceback (most recent call last):
  File "G:/Python源码/iterable_test.py", line 3, in <module>
    list.__next__()
AttributeError: 'list' object has no attribute '__next__'
print(type(list))
print(type(list_iterator))
<class 'list'>
<class 'list_iterator'>   

我们的list是一个可迭代的对象。可以调用iter(list)说明我们的list中肯定有__iter__()方法。

代码语言:javascript
复制
list_iterator = iter(list) 

list的源码中找到了这个方法。

代码语言:javascript
复制
  def __iter__(self, *args, **kwargs): # real signature unknown
        """ Implement iter(self). """
        pass

从下面这段报错代码中我们可以看出,list中一定是没有__next__()方法的。

代码语言:javascript
复制
list.__next__()
Traceback (most recent call last):
  File "G:/Python源码/iterable_test.py", line 3, in <module>
    list.__next__()

其实for循环中对于iterable对象有一个转换。

代码语言:javascript
复制
for x in [1,2,3,4,5]:
    pass

等价于===>

代码语言:javascript
复制
#先获取iterator对象
it = iter([1,2,3,4,5])
while True:
    try:
        #获取下一个值
        x = next(it);
    except StopIteration:
        # 遇到StopIteration就退出循环
        break

c

如何判断Iterable和Iterator

可以使用isinstance()判断一个对象是否是Iterable,Iterator对象:

代码语言:javascript
复制
from collections import Iterable,Iterator
list = [1,2,3,4]
list_iterator = iter(list)
print(isinstance(list,Iterable),isinstance(list,Iterator))#True False
print(isinstance(list_iterator,Iterable),isinstance(list_iterator,Iterator))#True True

从上面我们也可以看出。迭代器(iterator)一定是可迭代对象,但是可迭代对象(iterable)不一定。 d

如何自定义一个迭代器?

代码语言:javascript
复制
class EvenIterators(object):
    def __init__(self,n):
        self.stop = n
        self.value = -2
    #实现__iter__()方法并返回自身(因为迭代器[实现__iter__和__next__])
    def __iter__(self):
        return self
    def __next__(self):
        if self.value+2 > self.stop:
            raise StopIteration
        self.value += 2
        return self.value

e = EvenIterators(10)
print(e.__next__())
print(e.__next__())
print(e.__next__())
print(e.__next__())

for en in e:
    print(en)

上面的EvenIterators类实现了一个偶数迭代器。从这个例子我们可以看出,只要我们实现了迭代协议,即方法__iter__()和next(),我们就实现了iterator。

e

关于迭代问题

那什么是关于迭代问题呢?我们可以先看一下下面这段代码:

代码语言:javascript
复制
list = [1,2,3,4]
list_iterator = iter(list)

for item in list_iterator:
    print("第一次打印--",item)

for item in list_iterator:
    print("第二次打印--",item)

第一次打印-- 1
第一次打印-- 2
第一次打印-- 3
第一次打印-- 4

从上面可以看出,我的迭代器用完了就没有了。上面使用了第二个for循环没有打印出什么东西来,其实如果使用__next__()方法,没有数据的话也会抛出异常。那我们怎么去解决这个问题呢?我们可能想到我创建另一个迭代器,然后去遍历另外一个迭代器。但其实赋值赋给的是地址值,说白了就是他们访问的是同一块内存地址。这样我们就很清楚如何去解决了。因为list中没有其他的引用类型,所以这个时候使用浅copy和深copy都能解决问题。(当时不能直接使用list_iterator.copy()这种浅复制,因为会抛出没有这个方法的异常,也就是说iterator中没有这个copy()方法)

代码语言:javascript
复制
list = [1,2,3,4]
list_iterator = iter(list)
list_iterator2 = list_iterator

print(list_iterator.__next__())
print(list_iterator2.__next__())
print(list_iterator.__next__())
print(list_iterator2.__next__())

我们不能对迭代器进行切片浅赋值,也不能直接调用copy方法进行浅复制(因为list中有,但是迭代器中没有对应的方法)。所以只能使用copy模块来进行浅复制和深复制。

  • 浅复制
代码语言:javascript
复制
import copy
list = [1,2,3,4]
list_iterator = iter(list)
list_iterator2 = copy.copy(list_iterator)

for item in list_iterator:
    print(item)

for item in list_iterator2:
    print(item)

1
2
3
4
1
2
3
4
  • 深复制
代码语言:javascript
复制
import copy
list = [1,2,3,4]
list_iterator = iter(list)
copy_list_iterator = copy.deepcopy(list_iterator)

for item in list_iterator:
    print(item)

for item in copy_list_iterator:
    print(item)

1
2
3
4
1
2
3
4

最后来一个转换图:

▲生成器和迭代器的关系图

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 AI机器学习与深度学习算法 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档