寻求懒惰化Python数据的指南

时间:2012-07-23 19:49:16

标签: python lazy-evaluation

注意:我根据评论和答案编辑了原始问题。

我的问题是,如果将大量Python数据输入到程序中,该数据如何变得懒惰,因此内存不会溢出?

例如,如果通过读入文件并将每行或每行的一部分附加到列表来构建列表,那么该列表是否是惰性的?换句话说,是否可以附加列表并且列表是懒惰的?是否附加到将整个文件读入内存的列表中?

据我所知,如果我想浏览该列表,我会编写一个生成器函数来保持访问权限。

触发这个问题的是最近的SO post

如果这些数据来自一个包含10M行的数据库表,就像我们的MySQL日常水表读取表一样,我不会在不知道如何使数据变得懒惰的情况下使用mysqldb fetchall()命令。相反,我会一次读一行。

但是如果我确实想要将内存中的数据内容作为一个懒惰的序列呢?我怎么用Python做呢?

鉴于我没有提供具有特定问题的源代码,我正在寻找的答案是指向Python文档或其他地方的指针或指针以解决此问题。

感谢。

5 个答案:

答案 0 :(得分:2)

Python中用于延迟呈现序列的机制是generators

  

Generators [sic]函数允许您声明一个行为类似于迭代器的函数,即它可以在for循环中使用。

答案 1 :(得分:1)

“懒惰”代码的基本思想是代码在需要数据之前不会获取数据。

例如,假设我正在编写一个复制文本文件的函数。将整个文件读入内存然后编写整个文件并不是很懒惰。使用.readlines()方法从所有输入行中构建列表也不会很懒。但是,一次读取一行然后在阅读后写下每一行都是懒惰的。

# non-lazy
with open(input_fname) as in_f, open(output_fname, "w") as out_f:
    bytes = in_f.read()
    out_f.write(bytes)

# also non-lazy
with open(input_fname) as in_f, open(output_fname, "w") as out_f:
    lines = in_f.readlines()
    for line in lines:
        out_f.write(line)

# lazy
with open(input_fname) as in_f, open(output_fname, "w") as out_f:
    for line in in_f:  # only gets one line at a time
        out_f.write(line) # write each line as we get it

为了让您的代码变得懒惰,Python允许您使用“生成器”。使用yield语句编写的函数是生成器。对于您的数据库示例,您可以编写一个从数据库中一次产生一行的生成器,然后您可以编写如下代码:

def db_rows(database_name):
    # code to open the database goes here
    # write a loop that reads rows
        # inside the loop, use yield on each row
        yield row
    # code to close the database goes here

for row in db_rows(database_name):
    # do something with the row

答案 2 :(得分:1)

列表几乎与懒惰相反。最好的例子是rangexrange之间的差异; range创建一个列表,而xrange懒惰地使用生成器为您提供所需的每个数字。

>>> total = 0
>>> for i in range(2**30):
    total += i

Traceback (most recent call last):
  File "<pyshell#18>", line 1, in <module>
    for i in range(2**30):
MemoryError
>>> print total
0
>>> for i in xrange(2**30):
    total += i
>>> print total
576460751766552576

许多将采取清单的地方也会取代发电机。这是真的,Python 3完全取消xrange,并用它来取代普通的range

>>> total2 = sum(xrange(2**30))
>>> print total2
576460751766552576

制作自己的发电机很容易:

>>> def myrange(n):
        i = 0
        while i < n:
            yield i
            i += 1
>>> sum(xrange(10))
45
>>> sum(myrange(10))
45
>>> myrange(10)
<generator object myrange at 0x02A2DDA0>

如果你确实需要一个清单,那也很容易。但当然它不再是懒惰。

>>> list(myrange(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

答案 3 :(得分:0)

如果您只想要可以迭代的东西,我会看看生成器:

PEP 255包含大量相关信息。

另一个选项,取决于文件类型是linecache模块。

答案 4 :(得分:0)

  

但是如果我确实想要将内存中的数据内容作为一个懒惰的序列呢?

以下是制作延迟序列的方法:不是存储项目,而是在请求时动态生成它们,但隐藏在[]语法后面。我一直忘记SQL API的工作原理,因此以下内容应理解为伪代码。

class Table(object):
    def __init__(self, db_cursor):
        self._cursor = db_cursor

    def __getitem__(self, i):
        return self._cursor.fetch_row(i)

    def __iter__(self):
        for i in xrange(len(self)):
            yield self[i]

    def __len__(self):
        return self._cursor.number_of_rows()

这可以在许多可以使用list的情况下使用,但实际上并不存储任何内容。根据需要添加缓存(取决于访问模式)。