zoukankan      html  css  js  c++  java
  • python——聊聊iterable,sequence和iterators

    ---------------------------------------------------------------前言------------------------------------------------------------------------------

    • iterable被认为是一类对象,这类对象能够一次返回它的一个成员(也就是元素)。是适合迭代的对象。
    • 实际上,任何具有__iter__()或__getitem__()方法的对象,Python就认为它是一个iterable。
    • Python大量内置的iterable类型,如: list,str,tuple,dict,file,xrange等。使用内置的iter()函数来生成iterator。iter(iterable)   ->  iterator object
    • Sequence的字面意思是序列。既然是序列,那么就应该有成员,成员之间是有序的且包含了若干成员。
    • sequence首先是iterable。如果这个iterable可以通过整数索引来访问其元素并可以获
      得其大小,那么它是sequence。
    • iterator对象就是且必须实现了迭代协议(iterator protocol【1】【2】)的对象。Python里的iterator实现了两个方法:

          __iter__()      # 返回迭代器本身

          __next__()    # Python2使用next()

    • iterable: 至少定义了__iter__()或__getitem__()方法的对象。
    • sequence: 至少定义了__len__()或者__getitem__()方法的对象。
    • iterator:至少定义__iter__()和__next__()法的对象。

    --------------------------------------------------------------正文------------------------------------------------------------------------------

    想必Python的老鸟们对于Python里的iterable,sequence和iterators应该非常了解,且能做到运用自如。
    但是对于刚刚开始学习Python的新手们来说,这三个玩意儿估计够让他们抓狂的。先看一段有趣的代码:

    • >>> l = ['a', 'b', 'c']

    • >>> l_iter = l.__iter__()

    • >>> l

    • ['a', 'b', 'c']

    • >>> l_iter

    • <list_iterator object at 0x7f40f2c46668>

    • >>> for e in l: print(e)

    • ...

    • a

    • b

      c

    • >>> for e in l: print(e)

    • ...

    • a

    • b

    • c

    • >>> l_iter.__next__()

    • 'a'

    • >>> l_iter.__next__()

    • 'b'

    • >>> for e in l_iter: print(e)

    • ...

    • c

    • >>> l_iter.__next__()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      StopIteration

    • >>> list(l_iter)

    • []

    从这段代码,可以看出一些特点:

    • list和由list生成的list_iterator都可以进行遍历。

    • list可以无限次的使用,而list_iterator只能使用一次。

    • list_iterator通过__next__()方法来获取下一个元素,且只能获取一次。

    • 当最后一个元素被获取后,如果想继续,StopIteration将会抛出

    其实在这里,list就是iterable(也是sequence),而l_iter则是iterator,为什么它们会有那些特点呢?下面将

    详细介绍它们及其之间的关系。

     

    • Iterable

    在Python里iterable被认为是一类对象,这类对象能够一次返回它的一个成员(也就是元素)。抽象一点就是
    适合迭代的对象。实际上,任何具有__iter__()或__getitem__()方法的对象,Python就认为它是一个iterable。
    Python里有大量内置的iterable类型,如: list,str,tuple,dict,file,xrange等。使用内置的iter()函数来生成
    iterator。即:
    iter(iterable)   ->  iterator object

    • Sequence

    Sequence的字面意思是序列。既然是序列,那么就应该有成员,成员之间是有序的且包含了若干成员。当然
    从理论上讲,序列的成员数可以是无限制的。那么将其抽象成数据类型后,便可定义为有序对象的集合。维基
    百科是这样定义sequence的:
    A sequence is an ordered list. Like a set, it contains members (also called elements, or terms).
    The number of ordered elements
    (possibly infinite) is called the length of the sequence.
    从定义可以看出sequence有三个特性:

    • 包含成员(元素)

    • 有序成员(有序)

    • 成员数量(大小)

    Python里这样定义sequence:

    An iterable which supports efficient element access using integer indices via the __getitem__()
    special method and defines a
    __len__() method that returns the length of the sequence.
    从定义里可以看出,sequence首先是iterable。如果这个iterable可以通过整数索引来访问其元素并可以获
    得其大
    小,那么它是sequence很显然,dict是iterable,但不是sequence,即使它有__getitem__() 和 __len__()
    这两种方法。因为它不是通过整数索引而是通过key来获取元素的。Python将sequence对象的三个特性变成了两
    个方法__getitem__()和 __len__()。通过这两个方法我们可以获得sequence对象的元素和大小。

    • Iterator

    Iterator(迭代器)存在于众多面向对象的程序设计语言中,它是一种经典的设计模式。迭代器模式提供一种访问
    有序访问聚合对象里元素的方法。具体到Python语言里,正如在我的前一篇博文《Iterators详解》介绍的,iterator
    对象就是且必须实现了迭代协议(iterator protocol【1】【2】)的对象。Python里的iterator实现了两个方法:

    • __iter__()      # 返回迭代器本身

    • __next__()    # Python2使用next() 

    通过iterable, sequence和iterator的定义可以看出这三者的密切关系。下图很好的诠释了它们之间的关系。

    很显然,Python认为sequence和iterator都是iterable。当然还有一部分non-sequence也被认为是iterable。例如,
    dictionary,set等。定义了__getitem__()和__len__()方法的non-sequence对象和sequence对象都可以用iter()来
    返回一个iterator。iter(X)是这样工作的(X是一个iterable):

    • 调用X的__iter__()方法来生成一个iterator。

    • 如果X没有__iter__()方法,Python将会自动构建一个iterator,并尝试通过X[0], X[1], X[2] ... 或X[key1],x[key2] ... 来获取元素。

    Iterable里,sequence和non-sequence的区别就在于访问元素的方式,通过整数索引(index)访问的是seqence,
    而不能通过整数索引访问的是non-sequence。其实,我们完全可以通过对象定义的方法来区分一个对象到底是iterable,
    sequence还是iterator。

    • iterable: 至少定义了__iter__()或__getitem__()方法的对象。

    • sequence: 至少定义了__len__()或者__getitem__()方法的对象。

    • iterator:至少定义__iter__()和__next__()法的对象。

    下面来看一些实例:

    • #!/usr/bin/env python

    • # -*- coding: utf -*-

    • class Train(object):

    • def __init__(self, cars):

    •         self.cars = cars

    • def __len__(self):

    • return self.cars

    • def __getitem__(self, key):

    • if key >= 0:

    •             index = key

      else:

    •             index = self.cars + key

    • if 0 <= index < len(self):

    • return 'Carriage #%s' % (index + 1)

      else:

    • raise IndexError('No carriage at #%s' % key)

    这里,我们定义了一个名为Train的类实现了一个火车的sequence。这个实现了sequence的两个关键的方法:

    __len__()和__getitem__(),实际上这两个方法被叫作sequence protocol。实现了这两个方法,也就实现了

    个immutable 【4】的seqence。

    • >>> from train import Train

    • >>> t = Train(4)

    • >>> t

    • <train.Train object at 0x7f491e457080>

      >>> t[0]

    • 'Carriage #1'

    • >>> len(t)

      4

    • >>> t.__len__()

    • 4

    • >>> t.__getitem__(2)

    • 'Carriage #3'

    • >>> t[3]

    • 'Carriage #4'

    • >>> t[-3]
      'Carriage #2'

    • >>> for i in t:
      ...     print(i)
      ...
      Carriage #1
      Carriage #2
      Carriage #3
      Carriage #4

    从上面的实验代码可以看出,我们可以对 t 进行遍历,获取元素和大小毫无疑问,这个t对象有__getitem__()

    方法,所以它也是iterable。我们可以将用t来生成新的list,tuple等对象,也可生成iterator对象。请看下面的代码:

    • >>> list(t)

    • ['Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4'] # 生成了一个list对象

    • >>> iter(t)

    • <iterator object at 0x7f491e4755f8>

    • >>> tuple(t)

    • ('Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4') # 生成了一个tuple对象

    • >>> t_iter = iter(t)          
      >>> t_iter
      <iterator object at 0x7f491e475da0>                          # 生成了一个iterator对象
      >>> "__iter__" and "__next__" in dir(t_iter) #
      True
      >>>

    那么iterator和iterable(非iterator的iterable)到底有什么区别呢?我先看下面的代码:

    • >>> t

    • <train.Train object at 0x7f491e457080>

    • >>> t[0]

    • 'Carriage #1'

    • >>> t[3]

    • 'Carriage #4'

    • >>> t[4]

    • Traceback (most recent call last):

    •   File "<stdin>", line 1, in <module>

    •   File "/home/pygeek/DEV/pyprj/reading/train.py", line 22, in __getitem__

    • raise IndexError('No carriage at #%s' % key)

    • IndexError: No carriage at #4

    • >>> t[2]

    • 'Carriage #3'

    • >>> t[1]

    • 'Carriage #2'

    • >>> for i in t:             # 第一次遍历

    • ... print(i)

    • ...

    • Carriage #1

    • Carriage #2

    • Carriage #3

    • Carriage #4

    • >>> for i in t:             # 第二次遍历

      ...     print(i)

      ...

      Carriage #1

      Carriage #2

      Carriage #3

      Carriage #4

    t 对象是sequence,当然它也是iterable,但是它不是iterator。我们通过索引或遍历访问元素。当索引超出t对象

    的长度范围时,IndexError将会被抛出。t对象可以被多次甚至无限次的使用但是,iterator确实有些不一样了。

    • >>> t_iter = iter(t)

    • >>> t_iter

    • <iterator object at 0x7f491c9ac4e0>

    • >>> t_iter[0]

      Traceback (most recent call last):

    •   File "<stdin>", line 1, in <module>

    • TypeError: 'iterator' object is not subscriptable

    • >>> t_iter.__next__()

    • 'Carriage #1'

    • >>> t_iter.__next__()

    • 'Carriage #2'

    • >>> t_iter.__next__()

    • 'Carriage #3'

    • >>> t_iter.__next__()

    • 'Carriage #4'

    • >>> t_iter.__next__()

    • Traceback (most recent call last):

    •   File "<stdin>", line 1, in <module>

    • StopIteration

    • >>> for i in t_iter:

    • ... print(i)

    • ...

    • >>>

    上面的代码说明,iterator不能通过索引来访问元素,但是可以用它的__next__()方法来访问元素;可进行遍历,

    但是只能完整遍历一次。当所有的元素都被访问过后,再调用__next__(),抛出的异常是StopIteration,而不是

    IndexError。此时,我们称iterator已经被耗尽,不能再进行遍历。实际上,iterator存储了元素的位置状态并可以

    通过调用__next__()访问下一个元素并更新内部的位置状态直到所有的元素都被访问。这些不同使得iterator对象

    会比list,tuple这样的iterable更有效率并占用更少的内存空间。我们可以用sys.getsizeof()来做一粗略的测试:

    • >>> t_iter = iter(t)

    • >>> t_iter

    • <iterator object at 0x7f491e479780>

    • >>> t_list = list(t)

    • >>> t_list

    • ['Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4']

    • >>> t_tuple = tuple(t)

    • >>> t_tuple

    • ('Carriage #1', 'Carriage #2', 'Carriage #3', 'Carriage #4')

    • >>> sys.getsizeof(t), sys.getsizeof(t_iter), sys.getsizeof(t_list), sys.getsizeof(t_tuple)

    • (56, 56, 120, 80)

    我们可以看到list对象比iterator对象的两倍还要大。所以,如果我们仅仅只是需要iterable的元素,而不需要一个

    完整的list的话,那么用iterator将更有效率并节省空间。

    -------------------------------------------------------------------后记------------------------------------------------------------------

    简单总结一下:

    iterable: 实现了__iter__()或__getitem__()方法的对象。

    sequence:实现是了sequence protocol(即方法: __getitem__()和__len__()),并能使用整数索引访问元素的iterable对象。

    iterator: 实现了iterator protocol(即方法:__next__()和__iter__())的iterable对象。

    通过iterable创建iterator的方法:

    iter(iterable) -> iterator 

    iterable.__iter__()  -> iterator 【5】

    • 注:

     

    【1】所有的代码都在Python 3.4.0下测试通过。
    【2】protocol: a synonym  for interface used in dynamic languages like Python,Ruby,...

    【3】iterator protocol: The iterator protocol consists of two methods. The __iter__() method, which must return the iterator object and the __next__() method, which returns the next element.
    【4】immutable: An object with a fixed value. Such an object cannot be altered. A new object has to be created if a different value has to be stored.
    【5】iterable.__iter__()返回的是一个新的iterator,而iterator.__iter__()返回的iterator对象本身。

  • 相关阅读:
    mysql分页查询优化
    java反射及Method的Invoke方法(转载)
    java需会(转载)
    Java注解(Annotation)原理详解
    深入分析JDK动态代理
    Java并发编程:volatile关键字解析
    安装hadoop
    linux 配置ssh免密登录
    安装Centos 7 并且配置远程登录
    MAC安装VMware fusion
  • 原文地址:https://www.cnblogs.com/Simon-xm/p/3979161.html
Copyright © 2011-2022 走看看