Python核心编程学习笔记(1)
迭代器和iter()函数
1,定义:python2.2被加入,它为类序列对象提供了一个类序列接口,它们是一组数据结构,你可以利用它们的索引从0开始一直迭代到序列的最后一个条目.用“计数”的方法迭代序列是很简单的。Python的迭代无缝地支持序列对象,而且还允许程序员迭代非序列对象,包括用户自定义对象。
迭代器用起来很灵巧,你可以迭代不是序列但表现出序列行为的对象,例如字典的key,一个文件的行,等等,当你使用循环迭代一个对象条目时,你几乎不可能分辨出它是迭代器还是序列,你也不必关注这些,因为python让它像一个序列那样操作。
2,迭代器优势
a) 提供了可扩展的迭代器接口。
b) 对列表迭代带来了性能上的增强。
c) 在字典迭代中性能提升。
d) 创建真正的迭代接口,而不是原来的随机访问。
e) 与所有已经存在的用户定义的类以及扩展的模拟序列和映射的对象向后兼容。
f) 迭代非序列集合如映射和文件时可以创建更简洁可读的代码。
3,迭代方法
迭代器有一个next()方法的对象,而不是通过索引来计数,当你或是循环机制如for语句需要下一个项时,调用迭代器的next()方法就可以获得它。条目全部取出后,会引发一个StopIteration异常,这不是错误,是告诉外部调用者,迭代完成。
限制:不能向后移动,不能回到开始,也不能复制一个迭代器,若你要再次或者是同时迭代同个对象,你只能去创建另一个迭代器对象。不过这并不用担心,因为还有其他工具帮助你使用迭代器。
resersed()内建函数返回一个反序列的迭代器。Enumerate()内建函数也返回迭代器。此外两个新的内建函数,any()和all(),在版本2.5新增,若迭代器中某个/所有条目的值均为真则返回真。Python提供一整个itertools模块,包含各种有用的迭代器。
4,使用迭代器
a) 序列
>>> myTuple = (123,'xyz',45.67)
>>> i = iter(myTuple)
>>> i.next()
123
>>> i.next()
'xyz'
>>> i.next()
45.670000000000002
>>> i.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>>
正常使用时,用try-except捕获异常
>>> i = iter(myTuple)
>>> while True:
... try:
... j = i.next()
... except StopIteration:
... break;
... print j,
...
123 xyz 45.67
>>>
>>>
b) 字典
字典和文件是另外两个可迭代的python数据类型,字典的迭代会遍历它的键(keys)
语句for eachKey in myDict.keys()可以缩写为for eachKey inmyDict
>>> legends ={('Poe','author'):(1809,1849,1976),
... ('Gaudi','architect'):(1852,1906,1987),
...('Freud','psychoanalyst'):(1856,1939,1990)
... }
>>>
>>> for eachLegend in legends:
... print 'Name: %s\tOccupation: %s' % eachLegend
... print ' Birth: %s\tDeath:%s\tAlbum: %s\n' % legends[eachLegend]
...
Name: Poe Occupation: author
Birth: 1809 Death: 1849 Album: 1976
Name: Gaudi Occupation: architect
Birth: 1852 Death: 1906 Album: 1987
Name: Freud Occupation: psychoanalyst
Birth: 1856 Death: 1939 Album: 1990
>>>
>>>
此外,python还引进三个新的内建字典方法来定义迭代:myDict.iterkeys() (通过keys迭代)、myDict.itervalues()(通过values迭代)、myDict.iteritems()(通过key/value对迭代)。
In操作符也可以用于检查字典的key是否存在,布尔表达式myDict.has_key(anykey)可简写为anyKey inmyDict
c) 文件
文件对象生成的迭代器会自动调用readline()方法。可以访问文件的所有行,并且可以使用简单的for eachLine in myFile替换for eachLine in myFile.readlines():
>>> myFile = open('test.txt')
>>> for eachLine in myFile:
... print eachLine,
...
font-name: courier newfont-size: 10
>>>
>>> myFile.close()
>>>
5,创建迭代器
使用ietr()
a) Iter(obj)
b) Iter(func,sentinel)
若你传递一个参数给iter(),它会检查是不是序列,若是,则根据索引从0一直迭代序列结束。另一个创建迭代器的方法是使用类,一个实现了__iter__()和next()方法的类可以作为迭代器使用。
若是两个参数,它会重复调用func,直到迭代器的下个值等于sentinel
列表解析
1, 列表解析来自函数式编程Haskell,可以用来动态创建列表
2, 函数式编程特性,如lambda,map(),filter(),map()对所有的列表成员应用一个操作,filter()基于一个条件表达式过滤列表成员。最好lambda允许快速创建单行函数对象
语法:[expr for iter_var in iterable]
>>>map(lambda x: x** 2, range(6))
[0, 1, 4,9, 16, 25]
>>>
>>>[x ** 2 for x in range(6)] #效率更高
[0, 1, 4,9, 16, 25]
>>>
扩展语法:[expr for iter_var in iterable if cond_expr]
挑选序列中的奇数:
>>>seq = [11,10,9,9,10,10,9,8,23,9,7,18,12,11,12]
>>>filter(lambda x: x % 2, seq)
[11, 9,9, 9, 23, 9, 7, 11]
>>>
>>>[x for x in seq if x % 2]
[11, 9,9, 9, 23, 9, 7, 11]
>>>
>>>
迭代三行五列的矩阵:
>>> [(x+1,y+1) for x in range(3) for y in range(5)]
[(1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 2),(2, 3), (2, 4), (2, 5), (3, 1), (3, 2), (3, 3), (3, 4), (3, 5)]
>>>
计算文件所有非空白字符数目:
>>>f = open('test.txt','r')
>>>len([word for line in f for word in line.split()])
4
>>>
快速计算文件大小:
>>>os.stat('test.txt').st_size
35
>>>
>>>
>>>f.seek(0)
>>>sum([len(word) for line in f for word in line.split()])
32
>>>
>>>
seek(0)回到文件头部
生成器表达式
特定的函数,允许你返回一个值,然后“暂停”代码的执行,稍后回复
列表解析的不足是必要生成所有的数据,以创建整个列表,这可能对大量数据的迭代器有负面效应。生成器表达式通过结合列表解析和生成器解决了这个问题。
生成器与列表解析非常相似,语法恩相同。不过它并不真正创建数字列表,而是返回一个生成器,这个生成器在每次计算出一个条目后,把这个条目“产生”(yield)出来。生成器表达式使用“延迟计算”,所以它在使用内存上更有效。
列表解析:
[expr for ietr_var in iterable if cond_expr]
生成器表达式:
(expr for iter_var in ietrable id cond_expr)
生成器是一个内存使用更友好的结构
更高效的方法:
>>> f.seek(0)
>>> sum(len(word) for line in ffor word in line.split())
32
>>>
>>>
>>> rows = [1,2,3,17]
>>> def cols():
... yield 56
... yield 2
... yield 1
...
>>>
>>> pairs = ((i, j) for i in rowsfor j in cols())
>>> for pair in pairs:
... print pair
...
(1, 56)
(1, 2)
(1, 1)
(2, 56)
(2, 2)
(2, 1)
(3, 56)
(3, 2)
(3, 1)
(17, 56)
(17, 2)
(17, 1)
>>>
**改进一个寻找文件最长的行的程序代码:
>>> import os
>>> os.listdir(os.getcwd())
['LogAnaly.py', 'query_autoconf.py','insertSort.py', 'sdxl_efun_LogAnaly.pl', 'test.txt']
>>>
>>>
>>> f = open('/etc/motd','r')
>>> longest = 0
>>> while True:
... linelen = len(f.readline().strip())
... if not linelen:
... break
... if linelen > longest:
... longest = linelen
>>> f.close()
>>>return longest
更老一点:
Import string
..
len(string.strip(f.readline()))
首选方法:
f = open(‘/etc/passwd’,’r’)
longest = 0
allLines = f.readlines()
f.close()
for line in allLines:
linelen = len(line.strip())
if linelen >longest:
longest = linelen
return longest
再改进:
f = open(‘/etc/passwd’,’r’)
longest = 0
allLines = [x.strip() for x inf.readlines()]
f.close()
for line in allLines:
linelen = len(line)
if linelen >longest:
longest = linelen
return longest
但是以上处理大文件时会读取文件所有行,所以我们使用了迭代器,文件本身成为它的迭代器,不需要调用readlines()方法:
f = open(‘/etc/passwd’,’r’)
allLineLens = [len(x.strip()) for x in f]
f.close()
return max(allLineLens)
这里唯一问题是一行一行迭代f时,列表解析需要文件的所有行读取到内存中,然后生成列表,因此可以用生成器代替列表解析,然后移到max()函数里:
f = open(‘/etc/passwd’,’r’)
longest = max (len(x.strip()) for x in f)
f.close()
return longest
最后可简化为:
return max(len(x.strip()) for x in open(‘/etc/passwd’))
相关模块:itertools