Python标准库---十一、内置类型:迭代器类型、序列类型(list-typle-range)

上一篇文章: Python标准库---十、内置类型:数字类型
下一篇文章: Python标准库---十二、内置类型:文本序列类型(str)

## 迭代器类型
Python 支持在容器中进行迭代的概念。 这是经过使用两个单独方法来实现的;它们被用于容许用户自定义类对迭代的支持。 将在下文中详细描述的序列老是支持迭代方法。编程

容器对象要提供迭代支持,必须定义一个方法:segmentfault

container.__iter__()

返回一个迭代器对象。 该对象须要支持下文所述的迭代器协议。 若是容器支持不一样的迭代类型,则能够提供额外的方法来专门地请求不一样迭代类型的迭代器。 (支持多种迭代形式的对象的例子有同时支持广度优先和深度优先遍历的树结构。) 此方法对应于 Python/C API 中 Python 对象类型结构体的 tp_iter 槽位。app

迭代器对象自身须要支持如下两个方法,它们共同组成了 迭代器协议:函数

iterator.__iter__()

返回迭代器对象自己。 这是同时容许容器和迭代器配合 for 和 in 语句使用所必须的。 此方法对应于 Python/C API 中 Python 对象类型结构体的 tp_iter 槽位。spa

iterator.__next__()

从容器中返回下一项。 若是已经没有项可返回,则会引起 StopIteration 异常。 此方法对应于 Python/C API 中 Python 对象类型结构体的 tp_iternext 槽位。code

Python 定义了几种迭代器对象以支持对通常和特定序列类型、字典和其余更特别的形式进行迭代。 除了迭代器协议的实现,特定类型的其余性质对迭代操做来讲都不重要。对象

一旦迭代器的 __next__() 方法引起了 StopIteration,它必须一直对后续调用引起一样的异常。 不遵循此行为特性的实现将没法正常使用。排序

生成器类型

Python 的 generator 提供了一种实现迭代器协议的便捷方式。 若是容器对象 __iter__() 方法被实现为一个生成器,它将自动返回一个迭代器对象(从技术上说是一个生成器对象),该对象提供 __iter__() 和 __next__() 方法。 有关生成器的更多信息能够参阅 yield 表达式的文档。索引

序列类型 --- list, tuple, range

有三种基本序列类型:list, tuple 和 range 对象。 为处理 二进制数据 和 文本字符串 而特别定制的附加序列类型会在专门的小节中描述。接口

通用序列操做

大多数序列类型,包括可变类型和不可变类型都支持下表中的操做。 collections.abc.Sequence ABC 被提供用来更容易地在自定义序列类型上正确地实现这些操做。

此表按优先级升序列出了序列操做。 在表格中,s 和 t 是具备相同类型的序列,n, i, j 和 k 是整数,而 x 是任何知足 s 所规定的类型和值限制的任意对象。

in 和 not in 操做具备与比较操做相同的优先级。 + (拼接) 和 * (重复) 操做具备与对应数值运算相同的优先级。

clipboard.png

相同类型的序列也支持比较。 特别地,tuple 和 list 的比较是经过比较对应元素的字典顺序。 这意味着想要比较结果相等,则每一个元素比较结果都必须相等,而且两个序列长度必须相同。 (完整细节请参阅语言参考的 比较运算 部分。)

注释:

  1. 虽然 in 和 not in 操做在一般状况下仅被用于简单的成员检测,某些专门化序列 (例如 str, bytes 和 bytearray) 也使用它们进行子序列检测:
>>>

    >>> "gg" in "eggs"
    True
  1. 小于 0 的 n 值会被看成 0 来处理 (生成一个与 s 同类型的空序列)。

请注意序列 s 中的项并不会被拷贝;它们会被屡次引用。 这一点常常会令 Python 编程新手感到困扰;例如:

>>>

    >>> lists = [[]] * 3
    >>> lists
    [[], [], []]
    >>> lists[0].append(3)
    >>> lists
    [[3], [3], [3]]

具体的缘由在于 [[]] 是一个包含了一个空列表的单元素列表,因此 [[]] * 3 结果中的三个元素都是对这一个空列表的引用。 修改 lists 中的任何一个元素实际上都是对这一个空列表的修改。 你能够用如下方式建立以不一样列表为元素的列表:

>>>

    >>> lists = [[] for i in range(3)]
    >>> lists[0].append(3)
    >>> lists[1].append(5)
    >>> lists[2].append(7)
    >>> lists
    [[3], [5], [7]]

    进一步的解释能够在 FAQ 条目 如何建立多维列表? 中查看。
  1. 若是 i 或 j 为负值,则索引顺序是相对于序列 s 的末尾: 索引号会被替换为 len(s) + i 或 len(s) + j。 但要注意 -0 仍然为 0。
  2. s 从 i 到 j 的切片被定义为全部知足 i <= k < j 的索引号 k 的项组成的序列。 若是 i 或 j 大于 len(s),则使用 len(s)。 若是 i 被省略或为 None,则使用 0。 若是 j 被省略或为 None,则使用 len(s)。 若是 i 大于等于 j,则切片为空。
  3. s 从 i 到 j 步长为 k 的切片被定义为全部知足 0 <= n < (j-i)/k 的索引号 x = i + nk 的项组成的序列。 换句话说,索引号为 i, i+k, i+2k, i+3*k,以此类推,当达到 j 时中止 (但必定不包括 j)。 当 k 为正值时,i 和 j 会被减至不大于 len(s)。 当 k 为负值时,i 和 j 会被减至不大于 len(s) - 1。 若是 i 或 j 被省略或为 None,它们会成为“终止”值 (是哪一端的终止值则取决于 k 的符号)。 请注意,k 不可为零。 若是 k 为 None,则看成 1 处理。
  4. 拼接不可变序列老是会生成新的对象。 这意味着经过重复拼接来构建序列的运行时开销将会基于序列总长度的乘方。 想要得到线性的运行时开销,你必须改用下列替代方案之一:
  • 若是拼接 str 对象,你能够构建一个列表并在最后使用 str.join() 或是写入一个 io.StringIO 实例并在结束时获取它的值
  • 若是拼接 bytes 对象,你能够相似地使用 bytes.join() 或 io.BytesIO,或者你也可使用 bytearray 对象进行原地拼接。 bytearray 对象是可变的,而且具备高效的重分配机制
  • 若是拼接 tuple 对象,请改成扩展 list 类
  • 对于其它类型,请查看相应的文档
  1. 某些序列类型 (例如 range) 仅支持遵循特定模式的项序列,所以并不支持序列拼接或重复。
  2. 当 x 在 s 中找不到时 index 会引起 ValueError。 不是全部实现都支持传入额外参数 i 和 j。 这两个参数容许高效地搜索序列的子序列。 传入这两个额外参数大体至关于使用 s[i:j].index(x),可是不会复制任何数据,而且返回的索引是相对于序列的开头而非切片的开头。

不可变序列类型

不可变序列类型广泛实现而可变序列类型未实现的惟一操做就是对 hash() 内置函数的支持。

这种支持容许不可变类型,例如 tuple 实例被用做 dict 键,以及存储在 set 和 frozenset 实例中。

尝试对包含有不可哈希值的不可变序列进行哈希运算将会致使 TypeError。

可变序列类型

如下表格中的操做是在可变序列类型上定义的。 collections.abc.MutableSequence ABC 被提供用来更容易地在自定义序列类型上正确实现这些操做。

表格中的 s 是可变序列类型的实例,t 是任意可迭代对象,而 x 是符合对 s 所规定类型与值限制的任何对象 (例如,bytearray 仅接受知足 0 <= x <= 255 值限制的整数)。

clipboard.png

注释:

  1. t 必须与它所替换的切片具备相同的长度。
  2. 可选参数 i 默认为 -1,所以在默认状况下会移除并返回最后一项。
  3. 当在 s 中找不到 x 时 remove 操做会引起 ValueError。
  4. 当反转大尺寸序列时 reverse() 方法会原地修改该序列以保证空间经济性。 为提醒用户此操做是经过间接影响进行的,它并不会返回反转后的序列。
  5. 包括 clear() 和 copy() 是为了与不支持切片操做的可变容器 (例如 dict 和 set) 的接口保持一致

    3.3 新版功能: clear() 和 copy() 方法。

  6. n 值为一个整数,或是一个实现了 __index__() 的对象。 n 值为零或负数将清空序列。 序列中的项不会被拷贝;它们会被屡次引用,正如 通用序列操做 中有关 s * n 的说明。

列表

列表是可变序列,一般用于存放同类项目的集合(其中精确的类似程度将根据应用而变化)。

class list([iterable])

能够用多种方式构建列表:
  1. 使用一对方括号来表示空列表: []
  2. 使用方括号,其中的项以逗号分隔: [a], [a, b, c]
  3. 使用列表推导式: [x for x in iterable]
  4. 使用类型的构造器: list() 或 list(iterable)

构造器将构造一个列表,其中的项与 iterable 中的项具备相同的的值与顺序。 iterable 能够是序列、支持迭代的容器或其它可迭代对象。 若是 iterable 已是一个列表,将建立并返回其副本,相似于 iterable[:]。 例如,list('abc') 返回 ['a', 'b', 'c'] 而 list( (1, 2, 3) ) 返回 [1, 2, 3]。 若是没有给出参数,构造器将建立一个空列表 []。

其它许多操做也会产生列表,包括 sorted() 内置函数。

列表实现了全部 通常 和 可变 序列的操做。 列表还额外提供了如下方法:

#### sort(*, key=None, reverse=False)

此方法会对列表进行原地排序,只使用 < 来进行各项间比较。 异常不会被屏蔽 —— 若是有任何比较操做失败,整个排序操做将失败(而列表可能会处于被部分修改的状态)。

sort() 接受两个仅限以关键字形式传入的参数 (仅限关键字参数):

key 指定带有一个参数的函数,用于从每一个列表元素中提取比较键 (例如 key=str.lower)。 对应于列表中每一项的键会被计算一次,而后在整个排序过程当中使用。 默认值 None 表示直接对列表项排序而不计算一个单独的键值。

可使用 functools.cmp_to_key() 将 2.x 风格的 cmp 函数转换为 key 函数。

reverse 为一个布尔值。 若是设为 True,则每一个列表元素将按反向顺序比较进行排序。

当顺序大尺寸序列时此方法会原地修改该序列以保证空间经济性。 为提醒用户此操做是经过间接影响进行的,它并不会返回排序后的序列(请使用 sorted() 显示地请求一个新的已排序列表实例)。

sort() 方法确保是稳定的。 若是一个排序确保不会改变比较结果相等的元素的相对顺序就称其为稳定的 --- 这有利于进行多重排序(例如先按部门、再接薪级排序)。

CPython implementation detail: 在一个列表被排序期间,尝试改变甚至进行检测也会形成未定义的影响。 Python 的 C 实现会在排序期间将列表显示为空,若是发现列表在排序期间被改变将会引起 ValueError。

元组

元组是不可变序列,一般用于储存异构数据的多项集(例如由 enumerate() 内置函数所产生的二元组)。 元组也被用于须要同构数据的不可变序列的状况(例如容许存储到 set 或 dict 的实例)。

class tuple([iterable])

能够用多种方式构建元组:
  1. 使用一对圆括号来表示空元组: ()
  2. 使用一个后缀的逗号来表示单元组: a, 或 (a,)
  3. 使用以逗号分隔的多个项: a, b, c or (a, b, c)
  4. 使用内置的 tuple(): tuple() 或 tuple(iterable)

构造器将构造一个元组,其中的项与 iterable 中的项具备相同的值与顺序。 iterable 能够是序列、支持迭代的容器或其余可迭代对象。 若是 iterable 已是一个元组,会不加改变地将其返回。 例如,tuple('abc') 返回 ('a', 'b', 'c') 而 tuple( [1, 2, 3] ) 返回 (1, 2, 3)。 若是没有给出参数,构造器将建立一个空元组 ()。

请注意决定生成元组的实际上是逗号而不是圆括号。 圆括号只是可选的,生成空元组或须要避免语法歧义的状况除外。 例如,f(a, b, c) 是在调用函数时附带三个参数,而 f((a, b, c)) 则是在调用函数时附带一个三元组。

元组实现了全部 通常 序列的操做。

对于经过名称访问相比经过索引访问更清晰的异构数据多项集,collections.namedtuple() 多是比简单元组对象更为合适的选择。

range 对象

range 类型表示不可变的数字序列,一般用于在 for 循环中循环指定的次数。

class range(stop)

class range(start, stop[, step])

range 构造器的参数必须为整数(能够是内置的 int 或任何实现了 __index__ 特殊方法的对象)。 若是省略 step 参数,其默认值为 1。 若是省略 start 参数,其默认值为 0,若是 step 为零则会引起 ValueError。

若是 step 为正值,肯定 range r 内容的公式为 r[i] = start + step*i 其中 i >= 0 且 r[i] < stop。

若是 step 为负值,肯定 range 内容的公式仍然为 r[i] = start + step*i,但限制条件改成 i >= 0 且 r[i] > stop.

若是 r[0] 不符合值的限制条件,则该 range 对象为空。 range 对象确实支持负索引,可是会将其解读为从正索引所肯定的序列的末尾开始索引。

元素绝对值大于 sys.maxsize 的 range 对象是被容许的,但某些特性 (例如 len()) 可能引起 OverflowError。

一些 range 对象的例子:
>>> list(range(10))
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    >>> list(range(1, 11))
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    >>> list(range(0, 30, 5))
    [0, 5, 10, 15, 20, 25]
    >>> list(range(0, 10, 3))
    [0, 3, 6, 9]
    >>> list(range(0, -10, -1))
    [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
    >>> list(range(0))
    []
    >>> list(range(1, 0))
    []
range 对象实现了 通常 序列的全部操做,但拼接和重复除外(这是因为 range 对象只能表示符合严格模式的序列,而重复和拼接一般都会违反这样的模式)。

start

    start 形参的值 (若是该形参未提供则为 0)

stop

    stop 形参的值

step

    step 形参的值 (若是该形参未提供则为 1)

range 类型相比常规 list 或 tuple 的优点在于一个 range 对象老是占用固定数量的(较小)内存,不论其所表示的范围有多大(由于它只保存了 start, stop 和 step 值,并会根据须要计算具体单项或子范围的值)。

range 对象实现了 collections.abc.Sequence ABC,提供如包含检测、元素索引查找、切片等特性,并支持负索引 (参见 序列类型 --- list, tuple, range):

>>> r = range(0, 20, 2)
>>> r
range(0, 20, 2)
>>> 11 in r
False
>>> 10 in r
True
>>> r.index(10)
5
>>> r[5]
10
>>> r[:5]
range(0, 10, 2)
>>> r[-1]
18

使用 == 和 != 检测 range 对象是否相等是将其做为序列来比较。 也就是说,若是两个 range 对象表示相同的值序列就认为它们是相等的。 (请注意比较结果相等的两个 range 对象可能会具备不一样的 start, stop 和 step 属性,例如 range(0) == range(2, 1, 3) 而 range(0, 3, 2) == range(0, 4, 2)。)

在 3.2 版更改: 实现 Sequence ABC。 支持切片和负数索引。 使用 int 对象在固定时间内进行成员检测,而不是逐一迭代全部项。

在 3.3 版更改: 定义 '==' 和 '!=' 以根据 range 对象所定义的值序列来进行比较(而不是根据对象的标识)。

3.3 新版功能: start, stop 和 step 属性。

参见

linspace recipe 演示了如何实现一个延迟求值版本的适合浮点数应用的 range 对象。
上一篇文章: Python标准库---十、内置类型:数字类型
下一篇文章: Python标准库---十二、内置类型:文本序列类型(str)
相关文章
相关标签/搜索