Python迭代器、列表解析、生成器表达式、产生偏移和元素用法详解
1、Python迭代器
迭代器(iterator)有时又称游标(cursor),是程式设计的软件设计模式,可在容器物件(container,例如链表或阵列)上遍访的接口,设计人员无需关心容器物件的内存分配的实现细节。
各种语言实作迭代器的方式皆不尽同,有些面向对象语言,像Java, C#, Ruby, Python, Delphi都已将迭代器的特性内建语言当中,完美的跟语言整合,我们称之隐式迭代器(implicit iterator),但像C++语言本身就没有迭代器的特色,但STL仍利用模板实作了功能强大的迭代器。STL容器的数据的内存地址可能会重新分配(reallocate),与容器绑定的迭代器仍然可以定位到重新分配后的正确的内存地址。
迭代器另一方面还可以整合生成器(generator)。有些语言将二者视为同一接口,有些语言则将之独立化。
在 Python 中,迭代器是遵循迭代协议的对象。使用 iter() 从任何序列对象中得到迭代器(如 list, tuple, dictionary, set 等)。另一种形式的输入迭代器是generator。
要使得迭代器指向下一个元素,则使用成员函数next()(Python 2)或函数__next__()(Python 3)。当没有元素时,则引发 StopIteration异常。若要实现迭代器,则需要在类中定义next()(Python 2)或__next__()( Python 3)。
例:Python迭代器使用示例
In [1]: l1 = [1,2,3]
In [2]: i1 = l1.__iter__()
In [3]: i1.next()
Out[3]: 1
In [4]: i1.next()
Out[4]: 2
In [5]: i1.next()
Out[5]: 3
In [6]: i1.next()
---------------------------------------------------------------------------
StopIteration Traceback (mostrecent call last)
<ipython-input-6-be7912f76fe0> in<module>()
----> 1 i1.next()
StopIteration:
2、Python列表解析在需要改变列表而不是需要新建某列表时,可以使用列表解析。
1)、语法格式
[expr for iter_var in iterable]
[expr for iter_var in iterable if cond_expr]
第一种语法:首先迭代iterable里所有内容,每一次迭代,都把iterable里相应内容放到iter_var中,再在表达式中应用该iter_var的内容,最后用表达式的计算值生成一个列表。
第二种语法:加入了判断语句,只有满足条件的内容才把iterable里相应内容放到iter_var中,再在表达式中应用该iter_var的内容,最后用表达式的计算值生成一个列表。
2)、用法示例
例1:不加条件的列表解析示例
In [1]: l1 = [1,2,3,4,5]
In [2]: l2 = []
In [3]: for i in l1:
...: l2.append(i**2)
...:
In [4]: print l2
[1, 4, 9, 16, 25]
In [5]: t3 = [ i**2 for i in l1 ]
In [6]: print t3
[1, 4, 9, 16, 25]
In [7]: [ i**2 for i in l1 ]
Out[7]: [1, 4, 9, 16, 25]
例2:加入条件的列表解析示例
In [8]: l3 = [ i**2 for i in l1 if i >=3 ]
In [9]: print l3
[9, 16, 25]
In [10]: for i in [ i**2 for i inrange(1,11) ]: print i/2,
0 2 4 8 12 18 24 32 40 50
In [11]: for i in [ i**2 for i inrange(1,11) if i % 2 == 0 ]: print i/2,
2 8 18 32 50
例3:列表解析应用1:使用列表解析打印/var/log目录下以'.log'结尾的文件
In [1]: import os
In [2]: files = os.listdir('/var/log')
In [3]: s1 = 'test.log'
In [4]: s1.endswith('.log')
Out[4]: True
In [5]: s2 = 'test'
In [6]: s2.endswith('.log')
Out[6]: False
In [7]: logs = [ i for i in fil
%%file file files filter
In [7]: logs = [ i for i in files ifi.endswith('.log') ]
In [8]: print logs
['Xorg.9.log', 'Xorg.1.log','keepalived.log', 'anaconda.ifcfg.log', 'pm-powersave.log','anaconda.storage.log', 'mysqld.log', 'boot.log', 'anaconda.log', 'dracut.log','Xorg.0.log', 'anaconda.yum.log', 'wpa_supplicant.log', 'yum.log','Xorg.2.log', 'anaconda.program.log']
例4:列表解析应用2:根据已经有列表,高效生成新列表的方式
In [1]: l1 = ['x','y','z'];l2 = [1,2,3]
In [2]: l3 = [ (i,j) for i in l1 for j inl2 ]
In [3]: print l3
[('x', 1), ('x', 2), ('x', 3), ('y', 1),('y', 2), ('y', 3), ('z', 1), ('z', 2), ('z', 3)]
In [4]: l4 = [ (i,j) for i in l1 for j inl2 if j != 1 and i != 'x' ]
In [5]: print l4
[('y', 2), ('y', 3), ('z', 2), ('z', 3)]
3、Python生成器表达式
生成器表达式是在python2.4中引入的,当序列过长,而每次只需要获取一个元素时,
应当考虑使用生成器表达式而不是列表解析。生成器表达式的语法和列表解析一样,只不过生成器表达式是被()括起来的,而不是[]
生成器表达式并不真正创建数字列表,而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来。生成器表达式使用了“惰性计算”(lazy evaluation,也有翻译为“延迟求值”,
这种按需调用call by need的方式翻译为惰性更好一些),只有在检索时才被赋值( evaluated),
所以在列表比较长的情况下使用内存上更有效。
1)、语法格式(expr for iter_var in iterable)
(expr for iter_var in iterable ifcond_expr)
2)、用法示例
例1:Python生成器表达式用法示例
In [1]: y1 = ( i**2 for i in range(1,4))
In [2]: y1.next()
Out[2]: 1
In [3]: y1.next()
Out[3]: 4
In [4]: y1.next()
Out[4]: 9
In [5]: y1.next()
---------------------------------------------------------------------------
StopIteration Traceback (mostrecent call last)
<ipython-input-5-e528b641d5f6> in<module>()
----> 1 y1.next()
StopIteration:
例2:Python生成器对象在for循环中的使用
In [6]: for j in ( i**2 for i inrange(1,11)):print j/2,
0 2 4 8 12 18 24 32 40 50
4、Python产生偏移和元素
1)、生成器对象生成函数enumerate()
range()函数可在非完备遍历中用于生成索引偏移,而非偏移处的元素
enumerate()函数同时产生偏移索引和偏移元素,此内置函数返回一个生成器对象
enumerate(iterable[, start])
2)、用法示例:
例1:自定义url,使用enumerate()函数打印由索引和单个字符构成的元组
In [1]: url = 'web2.field.com'
In [2]: enumerate(url)
Out[2]: <enumerate at 0x11d1140>
In [3]: g1 = enumerate(url)
In [4]: g1.next()
Out[4]: (0, 'w')
In [5]: g1.next()
Out[5]: (1, 'e')
In [6]: g1.next()
Out[6]: (2, 'b')
例2:使用range()函数打印1~10中的奇数
In [7]: range(1,11,2)
Out[7]: [1, 3, 5, 7, 9]
例3:enumerate()函数偏移索引用法
In [8]: g1 = enumerate(url,4)
In [9]: g1.next()
Out[9]: (4, 'w')
In [10]: g1.next()
Out[10]: (5, 'e')
In [11]: g1.next()
Out[11]: (6, 'b')
5、总结说明:
1. 当只是需要执行一个循环的时候尽量使用循环而不是列表解析,这样更符合python提倡的直观性。
for item in sequence:
process(item)
2. 当有内建的操作或者类型能够以更直接的方式实现的,不要使用列表解析。
例如复制一个列表时,使用:L1=list(L)即可,不必使用:
L1=[x for x in L]
3. 当序列过长, 而每次只需要获取一个元素时,使用生成器表达式。
4. 列表解析的性能相比要比map要好,实现相同功能的for循环效率最差(和列表解析相比差两倍)。
5. 列表解析可以转换为for循环或者使用map(其中可能会用到filter、lambda函数)表达式,但是列表解析更为简单明了,后者会带来更复杂和深层的嵌套。