Python 3中的迭代器

3 投票
2 回答
1380 浏览
提问于 2025-04-17 20:37

在Python 3中,很多以前返回列表的函数(现在变成了类)现在返回的是可迭代对象,最常见的例子就是 range。在Python 3中,range被改成了可迭代对象,这样做是为了提高性能和节省内存(因为你不再需要先创建一个列表)。

其他一些“新”的可迭代对象包括 mapenumeratezip,以及字典操作的输出 dict.keys()dict.values()dict.items()。(可能还有更多,但我不太清楚)。

其中一些(比如 enumeratemap)通过变成可迭代对象,可能变得更节省内存。在Python 2.7中,其他的只是创建了已经在内存中的对象的列表,所以它们本来就很节省内存。

那么,为什么要把它们变成可迭代对象呢?每次想要排序等操作时还得转换成列表?

相关问题:

2 个回答

4

如果你想对它们进行排序,那么你需要把可迭代对象变成一个列表(这点sorted会帮你处理)……但是,你有多频繁需要对enumerate对象进行排序呢?相比之下,你更常见的情况是直接遍历它,对吧?那对于字典中的items,你是更常需要排序,还是只是遍历它们呢?

如果你的API生成的是一个懒惰的迭代器或者其他懒惰的可迭代对象,你可以用差不多同样的力气把它变成一个列表,就像你直接跳过迭代器生成一个列表一样。另一方面,如果你的API直接生成一个列表,那就没办法避免一次性把所有的项目都放在内存里。迭代器则更加灵活。

9

有几个原因:

  1. 现在字典操作返回的是字典视图对象,这些对象也可以像集合一样使用,让你在代码中有更多的选择。在Python 2中,你需要用dict.view*()方法来实现同样的功能。

  2. 在Python 2中,字典操作会生成一个新的列表对象;这个列表对象也会占用内存,即使它的索引指向的是已经存在的对象。还有一个副作用就是,列表的索引会增加所有字典内容的引用计数,这样会影响性能(可能还会清空CPU缓存)。

  3. zip()map()可以在任何可迭代对象上工作,包括生成器,但在使用时会把所有内容拉到一个大列表里。在Python 3中,它们变成了生成器,这样就不会自动消耗这些可迭代对象了。

需要注意的是,Python 2中的enumerate()从来没有返回过列表,它一直返回的是一个迭代器。

你可以通过对这些对象使用list()来获得旧版Python 2的行为。如果你需要排序的项目,可以对可迭代对象使用sorted()。但现在你有了选择,而不是被迫使用列表对象。

对于Python中的大多数使用场景,你其实并不需要一整个列表。你通常是对这些结果进行迭代。排序并不是最常见的使用场景,索引也不是。所以对于大多数情况来说,这个变化是有利的,让程序员可以用标准函数和类型写出更高效的代码。

撰写回答