列表解析和生成器表达式

列表解析(List comprehensions,或者缩略为list comps)来自函数式编程语言Haskell,可以用来动态创建列表。

[expr for iter_var in iterable]

       这个语法的核心是for 循环,他迭代iterable对象的所有条目,表达式expr应用于序列的每个成员,最后的结果值是该表达式产生的列表。

print map(lambda x:x ** 2,range(6))
print [x ** 2 for x in range(6)]            #使用列表解析

列表解析中,只有一次函数调用(调用range()函数),而第一条语句中有三次函数调用(range(),map(),lambda),也可以将x**2括起来,这样更便于阅读[(x**2) for x in range(6)],列表解析可以取代内建的map()和lambda函数,而且效率更高。结合if语句,列表解析还提供了一个扩展的语法形式。

[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]
print filter(lambda x:x%2,seq)
print [x for x in seq if x % 2]

还比如迭代一个矩阵

print [(x+1,y+1) for x in range(3) for y in range(5)]

       生成器表达式是列表解析的一个扩展,列表解析的一个重要不足之处在于要生成所有的数据,用于创建整个列表,这可能对于有大量数据的迭代器有负面效应,而生成器是特定的函数,允许返回一个值,然后“暂停”代码的执行,稍后回复。生成器表达式通过结合列表解析和生成器解决了整个问题。其语法和列表解析非常相似,而且他们的基本语法基本相同,不过生成器表达式并不真正的创建数字列表,而是返回一个生成器,整个生成器在每次计算出一个条目后,把这个条目“产生”出来,生成器表达式使用了“延迟计算”,所以在使用内存上更高效。

(expr for iter_var in iterable if cond_expr )

生成器并不会让列表解析废弃,它只是一个内存使用更友好的结构,基于此,很多使用生成器的地方

例如计算文件中的非空字符总和。

f = open('test.txt','r')
print len([word for line in f for word in line.split()])
print sum(len(word) for line in f for word in line.split())

为了避免创建庞大的列表,可以使用生成器表达式来完成对非空格字符的求解

这里寻找一个文件最长的行的例子来看看如何改进代码

f = open('/etc/motd','r')
longest = 0
while true:
    linelength = len(f.readline().strip())    #strip()函数是移除字符串首尾的空字符
    if not linelength:
        break
    if linelength > longest:
        longest = linelength
    f.close()
return longest

在上面的例子中,从读取文件开始一直查找到最长的行,一直占用着文件资源,那如果这是一个很多进程都需要访问的日志文件,这样理所当然的就不能一直占用文件的句柄不释放,因此作出如下改动。

f = open('/etc/motd','r')
longest = 0
allLines = f.readlines()
f.close()
for line in allLines:
    linlen = len(line.strip())
    if linlen > longest:
        longest = linlen    
return longest

列表解析允许稍微简化代码,而且可以再得到行的集合之前做一定的处理

f = open('/etc/motd','r')
longest = 0
allLines = [x.strip() for x in f.readlines()]
f.close()
for line in allLines:
    linlen = len(line)
    if linlen > longest:
        longest = linlen
return longest

然而两个例子中在处理大文件时候都有问题,因为readlines()函数会读取文件的所有行,在有了迭代器之后,文件本事就成为自己的迭代器,不需要调用readlines()函数,这样

f = open('/etc/motd','r')
allLineLen = [len(x.strip()) for x in f]
f.close()
return max(allLineLen)

这里的问题就是一行一行迭代f的时候,列表解析需要文件的所有行读取到内存中,然后生成列表,这里可以进一步简化代码:使用生成器表达式替换列表解析,然后把它移动大max()函数中

f = open('/etc/motd','r')
longest = max(len(x.strip()) for x in f)
f.close()
return longest

最后可以去掉文件的打开模式(默认为读取),然后让python去处理打开的文件。当然文件用于写入的时候不能这么做。

return max(len(x.strip()) for x in open('etc/moth'))


猜你喜欢

转载自blog.csdn.net/rhx_qiuzhi/article/details/80102339