0

リストを受け入れる関数を呼び出すとき、誰が責任を負いますか (呼び出し元 - ユーザー、または呼び出された - 関数list) generator

例:

>>> def print_collection(coll):
...     for element in coll:
...         print element

>>> def print_collection_twice(coll):
...     print_collection(coll)
...     print_collection(coll)

リストを使用すると、驚くことなく機能します。

>>> print_collection_twice( [x*2 for x in xrange(3)] )
0
2
4
0
2
4

また、ジェネレーターを使用すると、明らかに一度だけ出力されるため、厄介なバグにつながる可能性があります。

>>> print_collection_twice( (x*2 for x in xrange(3)) )
0
2
4

ここでのベストプラクティスは何ですか? 関数はリストを想定する必要があり、ユーザーは を提供する責任がありますか、それともユーザーが気にしないようにlist関数は常に最初に実行する必要がありますか?real_list = list(input_list)

編集

私は要素の型をチェックする方法を知ってassertおり、私の質問はかなり高レベルです

4

3 に答える 3

3

どちらのアプローチも防御可能です。必要な引数の種類を文書化するのは関数の責任であり、文書化と一致する引数を渡すのは呼び出し元の責任です。関数がリストが必要であると言い、ジェネレーターを渡した場合、それが機能するという保証はありません。

本当の問題は、関数が何を望んでいるのかということです。答えは、必要なものだけを言うべきであり、それ以上は言わないということです。したがって、本当に必要なのが反復可能である場合は、リストが必要だとは言わないでください。一般に、関数が一般的な反復可能オブジェクトにないリストの機能(たとえば、インデックス付け)を使用する必要がある場合は、それらの機能のみを使用する必要があります。誰かがそうでない引数を渡すと、当然例外が発生します。それらをサポートします。関数がこれらの機能を必要としない場合は、リストは必要ありません。

あなたの例は、引数を出力するだけなので、やや非現実的です。実生活では、ほとんどの場合、反復可能なものを消費する以外に何かをする必要があります。その「行う必要があること」の性質により、どのような議論を受け入れるべきかが明確になります。ただし、具体的な例としては、「はい」と言っlistてください(内部print_collection_twiceではなく内部print_collection)。その理由はprint_collection_twice、データを複数回使用したいためです。これは、一般的な反復可能オブジェクトでは不可能です。

于 2013-02-07T07:55:36.887 に答える
1

ベストプラクティスは、確かに必要なものを文書化することです。引数を反復可能にするかシーケンスにするかを文書化します。Pythonの哲学はダックタイピングを使用することであるため、引数をシーケンスであるかのように使用するようにしてください。

引数がシーケンスであるかどうかを確認する場合は、新しいリストを作成せずにそれを行う簡単な方法は、len組み込み関数を使用することです。

>>> len(iter([1,2,3]))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: object of type 'listiterator' has no len()

例外が発生した場合は、を呼び出すかlisttupleシーケンスを取得するか、例外を通過させてユーザーに処理させることができます。どの「ポリシー」を選択するかは異なり、完全にあなた次第です。Pythonプログラマーは、ドキュメントを注意深く読み、適切に機能する引数を渡す必要があります。そのため、引数として反復可能であり、常に呼び出しlistてシーケンスを取得することを表明するか、シーケンスが必要であり、オブジェクトが反復可能。iterableも許可する場合、引数をシーケンスにする必要があると述べる意味はわかりません。

ちなみに、単純に反復可能オブジェクトを複数回反復したい場合は、を使用できますitertools.tee

例えば:

def print_twice(iterable):
    old, new = itertools.tee(iterable)
    for element in old:
        # do stuff
    for element in new:
        # do stuff
于 2013-02-07T07:55:47.993 に答える
0

私の意見では、関数内のアプリケーションに依存します。重要なことは、関数がリストまたはイテレータのみを受け入れるかどうかを文書化することです。関数内での明示的なlist()呼び出しは、リスト/ジェネレーターを 1 回だけ反復処理する場合は不要な、長いリストの余分なオーバーヘッドを引き起こす可能性があります。

于 2013-02-07T07:52:40.473 に答える