返回迭代器的可怕的“节省空间”版本:
from itertools import tee
ay, by = [(r[i] for r in results) for i, results in enumerate(tee(map(calc, steps), 2))]
但基本上只是使用zip,因为大多数时候它不值得丑陋。
解释:
zip(*(calc(x) for x in steps))
将做得到(calc(x) for x in steps)一个迭代器[(2, 1), (4, 4), (6, 9), (8, 16), (10, 25)]。
当您打开包装时,您执行的操作相当于
zip((2, 1), (4, 4), (6, 9), (8, 16), (10, 25))
所以所有的项目都一次存储在内存中。证明:
def return_args(*args):
return args
return_args(*(calc(x) for x in steps))
#>>> ((2, 1), (4, 4), (6, 9), (8, 16), (10, 25))
因此,所有项目一次都在内存中。
那么我的工作原理是怎样的呢?
map(calc, steps)与(calc(x) for x in steps)(Python 3) 相同。这是一个迭代器。在 Python 2 上,使用imapor (calc(x) for x in steps)。
tee(..., 2)获得两个存储迭代差异的迭代器。如果您以同步方式迭代,tee则将占用O(1)内存。如果不这样做,则tee最多可以占用O(n). 所以现在我们有一个用法,可以让我们拥有到目前为止的O(1)内存。
enumerate显然会将其保存在不断的记忆中。
(r[i] for r in results)返回一个迭代器,它从每个结果中获取第ith项。这意味着在这种情况下,它会收到一对(依次为 so r=(2,1)、等)。r=(4,4)它返回特定的迭代器。
因此,如果您进行迭代ay并且by步调一致,将使用常量内存。内存使用量与迭代器之间的距离成正比。这在许多情况下很有用(想象一下对文件进行比较或诸如此类),但正如我大部分时间所说的那样,它不值得丑陋。还有一个额外的常数因子开销。