代码之家  ›  专栏  ›  技术社区  ›  octopusgrabbus ufukgun

寻找延迟化Python数据的指导原则

  •  1
  • octopusgrabbus ufukgun  · 技术社区  · 13 年前

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

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

    例如,如果一个列表是通过读取一个文件并将每一行或每一行的一部分附加到列表中来构建的,那么该列表是懒惰的吗?换言之,一个列表是否可以被附加到,而该列表是否是惰性的?追加到列表中是否会将整个文件读入内存?

    我知道,如果我想浏览这个列表,我会编写一个生成器函数来保持访问惰性。

    是什么引发了这个问题 SO post

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

    但是,如果我真的想把内存中数据的内容作为一个懒惰的序列呢?在Python中我该怎么做?

    考虑到我没有向源代码展示特定的问题,我要寻找的答案是指向Python文档中某个位置或其他地方的一个或多个指针,以解决这个问题。

    谢谢

    5 回复  |  直到 9 年前
        1
  •  2
  •   Ignacio Vazquez-Abrams    13 年前

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

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

        2
  •  1
  •   steveha    13 年前

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

    例如,假设我正在编写一个函数来复制一个文本文件。将整个文件读取到内存中,然后再写入整个文件,这不会是懒惰的。它也不会懒惰地使用 .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
    
        3
  •  1
  •   Mark Ransom    13 年前

    列表几乎是懒惰的反面。最好的例子是 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取消了 润智 完全,并用它来代替正常 范围

    >>> 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]
    
        4
  •  0
  •   mgilson    13 年前

    如果你只是想要一些可以迭代的东西,我会研究生成器:

    PEP 255 包含大量相关信息。

    另一个选项是 linecache 单元

        5
  •  0
  •   Fred Foo    13 年前

    但是,如果我真的想把内存中数据的内容作为一个懒惰的序列呢?

    以下是如何创建一个惰性序列:不存储项目,而是根据请求动态生成它们,但将其隐藏起来 [] 语法。我总是忘记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 可以使用,但实际上不存储任何东西。根据需要添加缓存(取决于访问模式)。