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对象本身。
python——聊聊iterable,sequence和iterators的更多相关文章
- Python序列(Sequence)
Sequence是Python的一种内置类型(built-in type),内置类型就是构建在Python Interpreter里面的类型,三种基本的Sequence Type是list(表),tu ...
- python 数据类型(sequence 序列、dictionary 词典、动态类型)
文章内容摘自:http://www.cnblogs.com/vamei 1.sequence 序列 sequence(序列)是一组有顺序的元素的集合 (严格的说,是对象的集合,但鉴于我们还没有引入“对 ...
- python中的sequence(序列)
摘要 这篇文章主要是为了让自己记住字典不是序列,python中序列的类型 序列化的定义 有个朋友问我,什么是序列化,我瞬间懵了,然后查了一下,发现廖雪峰老师给出了一个很舒服的解释: 序列化:我们把变量 ...
- Python:Iterable和Iterator
转于:https://blog.csdn.net/whgqgq/article/details/63685066 博主:gongqi1992 iterable和iterator最基本的区别: iter ...
- Python - 将iterable拆分成等长的数据块
说明 看文档发现一个有趣的应用(利用zip函数) 例如[1, 2, 3, 4] --> [(1, 2), (3, 4)],拆分成长度为2的数据块 Code >>> a = [1 ...
- python——字符串 & 正则表达
raw字符串(原始字符串) 所见即所得,例如r''My's\n'' Python转义字符 在需要在字符中使用特殊字符时,python用反斜杠(\)转义字符.如下表: 转义字符 描述 \(在行尾时) 续 ...
- Python展开一个嵌套的序列
摘自<Python Cookbook> 4.6 任务 序列中的子序列可能是序列,子序列的子项仍有可能是序列,以此类推,则序列嵌套可以达到任意的深度.需要循环遍历一个序列,将其所有的子序列展 ...
- python 递归展开嵌套的序列(生成器用法)
任何使用yield语句的函数都称为生成器.调用生成器函数将创建一个对象,该对象通过连续调用next()方法(在python3中是__next__())生成结果序列. next()调用使生成器函数一直运 ...
- Notes for "Python in a Nutshell"
Introduction to Python Wrap C/C++ libraries into Python via Cython and CFFI. Python implementations ...
随机推荐
- 「模板」 FHQ_Treap
「模板」 FHQ_Treap 我也是偶然发现我还没发过FHQ_Treap的板子. 那就发一波吧. 这个速度实在不算快,但是不用旋转,并且好写. 更重要的是,Splay 可以做的事情它都可以做!比如区间 ...
- HDU 1312 Red and Black (深搜)
题目链接 Problem Description There is a rectangular room, covered with square tiles. Each tile is colore ...
- 【CC2530入门教程-01】CC2530微控制器开发入门基础
[引言] 本系列教程就有关CC2530单片机应用入门基础的实训案例进行分析,主要包括以下6部分的内容:[1]CC2530微控制器开发入门基础.[2]通用I/O端口的输入和输出.[3]外部中断初步应用. ...
- js localtion.href 数据传输
1.今天发现的一种数据发送 如下标红 <script> <%--测试juquery的代码如下操作.我们可以看出使用juquery 进行选择标签的属性可以更加方便--%> con ...
- 21、python操作redis的模块?
什么是redis? redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(s ...
- MapperScannerConfigurer不 property-placeholder
关于org.mybatis.spring.mapper.MapperScannerConfigurer不支持 property-placeholder 参考了http://www.oschina.ne ...
- Java多线程学习(二)synchronized关键字(2)
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- AGC025简要题解
AGC025简要题解 B RGB Coloring 一道简单题,枚举即可. C Interval Game 考虑可以进行的操作只有两种,即左拉和右拉,连续进行两次相同的操作是没有用的. 左拉时肯定会选 ...
- ThinkPHP5 正则验证中有“|”时提示“规则错误”的解决方案
正则规则中有“|”时,会引起解析错误: 'regex:\d{3,4}[\s,-]?\d{7,8}|1[3,4,5,8]\d[\s,-]?\d{4}[\s,-]?\d{4}' 使用数组语法可以解决: [ ...
- MNIST数据集转化为二维图片
#coding: utf-8 from tensorflow.examples.tutorials.mnist import input_data import scipy.misc import o ...