77

たとえば、Python のファイルは反復可能です。ファイル内の行を反復処理します。行数を数えたい。

1 つの簡単な方法は、これを行うことです。

lines = len(list(open(fname)))

ただし、これはファイル全体を (一度に) メモリにロードします。これはむしろイテレータの目的を無効にします (現在の行をメモリに保持する必要があるだけです)。

これは機能しません:

lines = len(line for line in open(fname))

ジェネレーターには長さがありません。

カウント関数を定義する以外にこれを行う方法はありますか?

def count(i):
    c = 0
    for el in i: c += 1
    return c

明確にするために、ファイル全体を読み取る必要があることを理解しています! 一度にすべてをメモリに入れたくありません

4

10 に答える 10

87

反復可能で反復し、反復回数を数えることはできません。それが、リストではなく反復可能にする理由です。これは実際にはPython固有の問題ではありません。従来のリンクリストのデータ構造を見てください。長さを見つけることは、要素の数を見つけるためにリスト全体を反復することを含むO(n)操作です。

上記のmcruteのように、関数を次のように減らすことができます。

def count_iterable(i):
    return sum(1 for e in i)

もちろん、独自の反復可能なオブジェクトを定義している場合は、いつでも__len__自分で実装して、要素数をどこかに保つことができます。

于 2008-12-24T06:23:35.833 に答える
24

これを行うことができる行数が必要な場合は、それを行うためのより良い方法を知りません:

line_count = sum(1 for line in open("yourfile.txt"))
于 2008-12-24T06:03:06.747 に答える
16

このcardinalityパッケージは、count()イテラブルのサイズを数えてチェックするための効率的な関数といくつかの関連関数を提供します: http://cardinality.readthedocs.org/

import cardinality

it = some_iterable(...)
print(cardinality.count(it))

内部的にはenumerate()andcollections.deque()を使用して、実際のすべてのループおよびカウント ロジックを C レベルに移行し、forPython のループよりも大幅にスピードアップします。

于 2015-02-07T18:04:18.553 に答える
11

私はこの再定義をしばらくの間使用しました:

def len(thingy):
    try:
        return thingy.__len__()
    except AttributeError:
        return sum(1 for item in iter(thingy))
于 2008-12-24T07:49:02.383 に答える
9

この一般的な問題に対して実装されたソリューションがあることがわかりました。ilen()の関数の使用を検討してくださいmore_itertools

more_itertools.ilen(iterable)

ファイル内の行数を出力する例 (このwithステートメントを使用して、ファイルを閉じる処理を安全に処理します):

# Example
import more_itertools

with open("foo.py", "r+") as f:
    print(more_itertools.ilen(f))

# Output: 433

この例は、ファイル内の行を合計するために前に示したソリューションと同じ結果を返します。

# Equivalent code
with open("foo.py", "r+") as f:
    print(sum(1 for line in f))

# Output: 433
于 2016-12-12T17:48:12.220 に答える
8

イテラブルが有限であることが保証されていないという単純な理由から、絶対にそうではありません。

この完全に正当なジェネレータ関数を考えてみましょう:

def forever():
    while True:
        yield "I will run forever"

でこの関数の長さを計算しようとしても、len([x for x in forever()])明らかにうまくいきません。

お気づきのように、イテレーター/ジェネレーターの目的の多くは、すべてをメモリにロードすることなく、大きなデータセットで作業できるようにすることです。すぐに長さを取得できないという事実は、トレードオフと見なす必要があります。

于 2008-12-24T06:54:08.033 に答える
2

当時は重複に気づかなかったようですので、重複への回答の抜粋をここにも投稿します。

より大きな入力に対するスワップのスラッシングと再割り当てのオーバーヘッドを回避するために ( とは異なり)固定のメモリ オーバーヘッド動作を維持しながら、反復可能オブジェクトが長い場合よりも有意に高速に実行する方法がありsum(1 for i in it)ます (反復可能オブジェクトが短い場合でもそれほど遅くはありません) 。len(list(it))

# On Python 2 only, get zip that lazily generates results instead of returning list
from future_builtins import zip

from collections import deque
from itertools import count

def ilen(it):
    # Make a stateful counting iterator
    cnt = count()
    # zip it with the input iterator, then drain until input exhausted at C level
    deque(zip(it, cnt), 0) # cnt must be second zip arg to avoid advancing too far
    # Since count 0 based, the next value is the count
    return next(cnt)

と同様len(list(it))に、ilen(it)CPython で C コードのループを実行します ( 、dequeすべてC で実装されています)。通常、ループごとのバイト コードの実行を回避することが、CPython でのパフォーマンスの鍵となります。countzip

ここですべてのパフォーマンス数値を繰り返すのではなく、完全なパフォーマンスの詳細とともに私の回答を示します。

于 2018-11-08T15:26:37.873 に答える
0

We'll, if you think about it, how do you propose you find the number of lines in a file without reading the whole file for newlines? Sure, you can find the size of the file, and if you can gurantee that the length of a line is x, you can get the number of lines in a file. But unless you have some kind of constraint, I fail to see how this can work at all. Also, since iterables can be infinitely long...

于 2008-12-24T06:39:10.030 に答える