1

注:コメントと回答に基づいて元の質問を編集しました。

私の質問は、大量のPythonデータがプログラムに入力された場合、どのようにしてそのデータを遅延させ、メモリがオーバーフローしないようにすることができるかということです。

たとえば、ファイルを読み込んで各行または行の一部をリストに追加することによってリストを作成する場合、そのリストは怠惰ですか?言い換えれば、リストを追加してリストを怠惰にすることはできますか?リストに追加すると、ファイル全体がメモリに読み込まれますか?

そのリストをウォークスルーしたい場合は、アクセスを怠惰に保つためのジェネレーター関数を作成することを理解しています。

この質問のきっかけとなっているのは、この最近のSO投稿です

MySQLの毎日の水道メーター読み取りテーブルの1つなど、このデータが1,000万行のデータベーステーブルからのものである場合、データを遅延させる方法を知らずにmysqldb fetchall()コマンドを使用することはありません。代わりに、一度に1行ずつ読み取ります。

しかし、メモリ内のそのデータの内容を遅延シーケンスとして必要とした場合はどうなりますか?Pythonでどのように実行しますか?

私が特定の問題を抱えたソースコードを提示していないことを考えると、私が探している答えは、この問題を解決するためのPythonドキュメント内の場所または他の場所へのポインターです。

ありがとう。

4

5 に答える 5

2

シーケンスを遅延表示するためのPythonのメカニズムは、ジェネレーターです。

ジェネレーター[sic]関数を使用すると、イテレーターのように動作する関数を宣言できます。つまり、forループで使用できます。

于 2012-07-23T19:51:48.900 に答える
1

「レイジー」コードの基本的な考え方は、データが必要になるまでコードはデータを取得しないということです。

たとえば、テキストファイルをコピーする関数を作成しているとします。ファイル全体をメモリに読み込んでから、ファイル全体を書き込むのは怠惰ではありません。.readlines()また、このメソッドを使用してすべての入力行からリストを作成するのも面倒ではありません。ただし、一度に1行ずつ読み取り、読み取った後に各行を書き込むのは面倒です。

# 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はジェネレーターです。データベースの例では、データベースから一度に1行を生成するジェネレーターを記述してから、次のようなコードを記述できます。

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
于 2012-07-23T22:26:09.210 に答える
1

リストは怠惰のほとんど反対です。range最良の例はとの違いxrangeです。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]
于 2012-07-24T00:17:12.220 に答える
0

反復できるものが必要な場合は、ジェネレーターを調べます。

PEP 255には、多くの関連情報が含まれています。

ファイルのタイプに応じた別のオプションは、linecacheモジュールです。

于 2012-07-23T19:53:04.707 に答える
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使用できますが、実際には何も保存しません。必要に応じてキャッシュを追加します(アクセスパターンによって異なります)。

于 2012-07-23T23:47:13.427 に答える