《编写高质量Python代码的59个有效方法》——第9条:用生成器表达式来改写数据量较大的列表推导

第9条:用生成器表达式来改写数据量较大的列表推导
列表推导(参见本书第7条)的缺点是:在推导过程中,对于输入序列中的每个值来说,可能都要创建仅含一项元素的全新列表。当输入的数据比较少时,不会出问题,但如果输入的数据非常多,那么可能会消耗大量内存,并导致程序崩溃。
例如,要读取一份文件并返回每行的字符数。若采用列表推导来做,则需把文件每一行的长度都保存在内存中。如果这个文件特别大,或是通过无休止的network socket(网络套接字)来读取,那么这种列表推导就会出问题。下面的这段列表推导代码,只适合处理少量的输入值。

为了解决此问题,Python提供了生成器表达式(generator expression),它是对列表推导和生成器的一种泛化(generalization)。生成器表达式在运行的时候,并不会把整个输出序列都呈现出来,而是会估值为迭代器(iterator),这个迭代器每次可以根据生成器表达式产生一项数据。
把实现列表推导所用的那种写法放在一对圆括号中,就构成了生成器表达式。下面给出的生成器表达式与刚才的代码等效。二者的区别在于,对生成器表达式求值的时候,它会立刻返回一个迭代器,而不会深入处理文件中的内容。

以刚才返回的那个迭代器为参数,逐次调用内置的next函数,即可使其按照生成器表达式来输出下一个值。可以根据自己的需要,多次命令迭代器根据生成器表达式来生成新值,而不用担心内存用量激增。

使用生成器表达式还有个好处,就是可以互相组合。下面这行代码会把刚才那个生成器表达式所返回的迭代器用作另外一个生成器表达式的输入值。

外围的迭代器每次前进时,都会推动内部那个迭代器,这就产生了连锁效应,使得执行循环、评估条件表达式、对接输入和输出等逻辑都组合在了一起。

上面这种连锁生成器表达式,可以迅速在Python中执行。如果要把多种手法组合起来,以操作大批量的输入数据,那最好是用生成器表达式来实现。只是要注意:由生成器表达式所返回的那个迭代器是有状态的,用过一轮之后,就不要反复使用了(参见本书第17条)。
要点
当输入的数据量较大时,列表推导可能会因为占用太多内存而出问题。
由生成器表达式所返回的迭代器,可以逐次产生输出值,从而避免了内存用量问题。
把某个生成器表达式所返回的迭代器,放在另一个生成器表达式的for子表达式中,即可将二者组合起来。
串在一起的生成器表达式执行速度很快。

时间: 2024-09-26 20:38:06

《编写高质量Python代码的59个有效方法》——第9条:用生成器表达式来改写数据量较大的列表推导的相关文章

《编写高质量Python代码的59个有效方法》——导读

目 录 前 言 致 谢第1章 用Pythonic方式来思考 第1条:确认自己所用的Python版本 第2条:遵循PEP 8风格指南 第3条:了解bytes.str与unicode的区别第4条:用辅助函数来取代复杂的表达式第5条:了解切割序列的办法第6条:在单次切片操作内,不要同时指定start.end和stride第7条:用列表推导来取代map和f?ilter 第8条:不要使用含有两个以上表达式的列表推导第9条:用生成器表达式来改写数据量较大的列表推导第10条:尽量用enumerate取代ran

《编写高质量Python代码的59个有效方法》——第3条:了解bytes、str与unicode的区别

第3条:了解bytes.str与unicode的区别 Python 3有两种表示字符序列的类型:bytes和str.前者的实例包含原始的8位值:后者的实例包含Unicode字符. Python 2也有两种表示字符序列的类型,分别叫做str和unicode.与Python 3不同的是,str的实例包含原始的8位值:而unicode的实例,则包含Unicode字符. 把Unicode字符表示为二进制数据(也就是原始8位值)有许多种办法.最常见的编码方式就是UTF-8.但是大家要记住,Python 3

《编写高质量Python代码的59个有效方法》——第2条:遵循PEP 8风格指南

第2条:遵循PEP 8风格指南 <Python Enhancement Proposal #8>(8号Python增强提案)又叫PEP 8,它是针对Python代码格式而编订的风格指南.尽管可以在保证语法正确的前提下随意编写Python代码,但是,采用一致的风格来书写可以令代码更加易懂.更加易读.采用和其他Python程序员相同的风格来写代码,也可以使项目更利于多人协作.即便代码只会由你自己阅读,遵循这套风格也依然可以令后续的修改变得容易一些. PEP 8列出了许多细节,以描述如何撰写清晰的P

《编写高质量Python代码的59个有效方法》——第7条:用列表推导来取代map和f?ilter

第7条:用列表推导来取代map和f?ilterPython提供了一种精练的写法,可以根据一份列表来制作另外一份.这种表达式称为list comprehension(列表推导).例如,要用列表中每个元素的平方值构建另一份列表.如果采用列表推导来实现,那就同时指定制作新列表时所要迭代的输入序列,以及计算新列表中每个元素的值时所用的表达式. 除非是调用只有一个参数的函数,否则,对于简单的情况来说,列表推导要比内置的map函数更清晰.如果使用map,那就要创建lambda函数,以便计算新列表中各个元素的

《编写高质量Python代码的59个有效方法》——第8条:不要使用含有两个以上表达式的列表推导

第8条:不要使用含有两个以上表达式的列表推导除了基本的用法(参见本书第7条)之外,列表推导也支持多重循环.例如,要把矩阵(也就是包含列表的列表,即二维列表)简化成一维列表,使原来的每个单元格都成为新列表中的普通元素.这个功能采用包含两个for表达式的列表推导即可实现,这些for表达式会按照从左至右的顺序来评估. 上面这个例子简单易懂,这就是多重循环的合理用法.还有一种包含多重循环的合理用法,那就是根据输入列表来创建有两层深度的新列表.例如,我们要对二维矩阵中的每个单元格取平方,然后用这些平方值构

《编写高质量Python代码的59个有效方法》——第16条:考虑用生成器来改写直接返回列表的函数

第16条:考虑用生成器来改写直接返回列表的函数 如果函数要产生一系列结果,那么最简单的做法就是把这些结果都放在一份列表里,并将其返回给调用者.例如,我们要查出字符串中每个词的首字母,在整个字符串里的位置.下面这段代码,用append方法将这些词的首字母索引添加到result列表中,并在函数结束时将其返回给调用者. 输入一些范例值,以验证该函数能够正常运作: index_words函数有两个问题. 第一个问题是,这段代码写得有点拥挤.每次找到新的结果,都要调用append方法.但我们真正应该强调的

《编写高质量Python代码的59个有效方法》——第15条:了解如何在闭包里使用外围作用域中的变量

第15条:了解如何在闭包里使用外围作用域中的变量 假如有一份列表,其中的元素都是数字,现在要对其排序,但排序时,要把出现在某个群组内的数字,放在群组外的那些数字之前.这种用法在绘制用户界面时候可能会遇到,我们可以用这个办法把重要的消息或意外的事件优先显示在其他内容前面. 实现该功能的一种常见做法,是在调用列表的sort方法时,把辅助函数传给key参数.这个辅助函数的返回值,将会用来确定列表中各元素的顺序.辅助函数可以判断受测元素是否处在重要群组中,并据此返回相应的排序关键字(sort key).

《编写高质量Python代码的59个有效方法》——第12条:不要在for和while循环后面写else块

第12条:不要在for和while循环后面写else块 Python提供了一种很多编程语言都不支持的功能,那就是可以在循环内部的语句块后面直接编写else块. 奇怪的是,这种else块会在整个循环执行完之后立刻运行.既然如此,那它为什么叫做else呢?为什么不叫and?在if/else语句中,else的意思是:如果不执行前面那个if块,那就执行else块.在try/except语句中,except的定义也类似:如果前面那个try块没有成功执行,那就执行except块. 同理,try/except

《编写高质量Python代码的59个有效方法》——第13条:合理利用try/except/else/f?inally结构中的每个代码块

第13条:合理利用try/except/else/f?inally结构中的每个代码块Python程序的异常处理可能要考虑四种不同的时机.这些时机可以用try.except.else和f?inally块来表述.复合语句中的每个块都有特定的用途,它们可以构成很多种有用的组合方式(参见本书第51条).1.?f?inally块如果既要将异常向上传播,又要在异常发生时执行清理工作,那就可以使用try/f?inally结构.这种结构有一项常见的用途,就是确保程序能够可靠地关闭文件句柄(还有另外一种写法,参见