in
forループコンテキストでリスト内包表記またはキーワードを使用する場合、つまり:
for o in X:
do_something_with(o)
また
l=[o for o in X]
- 背後にあるメカニズムはどのように機能し
in
ますか? - 内のどの関数/メソッドを
X
呼び出しますか? X
複数の方法に準拠できる場合、優先順位は何ですか?X
リスト内包表記が速くなるように、効率的に書く方法は?
in
forループコンテキストでリスト内包表記またはキーワードを使用する場合、つまり:
for o in X:
do_something_with(o)
また
l=[o for o in X]
in
ますか?X
呼び出しますか?X
複数の方法に準拠できる場合、優先順位は何ですか?X
リスト内包表記が速くなるように、効率的に書く方法は?、afaik、完全で正しい答え。
for
、forループとリスト内包表記の両方で、を呼び出しiter()
ますX
。メソッドまたはメソッドがあるiter()
場合は、iterableを返します。両方を実装する場合は、が使用されます。どちらも持っていない場合は取得します。X
__iter__
__getitem__
__iter__
TypeError: 'Nothing' object is not iterable
これは:を実装し__getitem__
ます
class GetItem(object):
def __init__(self, data):
self.data = data
def __getitem__(self, x):
return self.data[x]
使用法:
>>> data = range(10)
>>> print [x*x for x in GetItem(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
これは実装の例です__iter__
:
class TheIterator(object):
def __init__(self, data):
self.data = data
self.index = -1
# Note: In Python 3 this is called __next__
def next(self):
self.index += 1
try:
return self.data[self.index]
except IndexError:
raise StopIteration
def __iter__(self):
return self
class Iter(object):
def __init__(self, data):
self.data = data
def __iter__(self):
return TheIterator(data)
使用法:
>>> data = range(10)
>>> print [x*x for x in Iter(data)]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
ご覧のとおり、イテレータを実装するには両方が必要であり、__iter__
それによってイテレータが返されます。
それらを組み合わせることができます:
class CombinedIter(object):
def __init__(self, data):
self.data = data
def __iter__(self):
self.index = -1
return self
def next(self):
self.index += 1
try:
return self.data[self.index]
except IndexError:
raise StopIteration
使用法:
>>> well, you get it, it's all the same...
ただし、一度に実行できるイテレータは1つだけです。OK、この場合、これを行うことができます:
class CheatIter(object):
def __init__(self, data):
self.data = data
def __iter__(self):
return iter(self.data)
__iter__
しかし、あなたはの方法を再利用しているだけなので、それはごまかしですlist
。より簡単な方法は、yieldを使用__iter__
して、ジェネレーターにすることです。
class Generator(object):
def __init__(self, data):
self.data = data
def __iter__(self):
for x in self.data:
yield x
これが私がお勧めする方法です。簡単で効率的。
X
反復可能でなければなりません。__iter__()
イテレータオブジェクトを返すを実装する必要があります。イテレータオブジェクトはnext()
、を実装する必要があります。これは、呼び出されるたびに次のアイテムを返すか、StopIteration
次のアイテムがない場合はを発生させます。
リスト、タプル、ジェネレーターはすべて反復可能です。
プレーンfor
オペレータは同じメカニズムを使用することに注意してください。
質問のコメントに答えるこの場合、ソースを読むことは最善の考えではないと言えます。コンパイルされたコード(ceval.c)の実行を担当するコードは、Pythonソースを初めて見る人にとってはそれほど冗長ではないようです。forループの反復を表すスニペットは次のとおりです。
TARGET(FOR_ITER)
/* before: [iter]; after: [iter, iter()] *or* [] */
v = TOP();
/*
Here tp_iternext corresponds to next() in Python
*/
x = (*v->ob_type->tp_iternext)(v);
if (x != NULL) {
PUSH(x);
PREDICT(STORE_FAST);
PREDICT(UNPACK_SEQUENCE);
DISPATCH();
}
if (PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(
PyExc_StopIteration))
break;
PyErr_Clear();
}
/* iterator ended normally */
x = v = POP();
Py_DECREF(v);
JUMPBY(oparg);
DISPATCH();
ここで実際に何が起こっているかを見つけるには、冗長性がそれほど良くない他のファイルの束に飛び込む必要があります。したがって、そのような場合、ドキュメントやSOのようなサイトが最初に行くべき場所であり、ソースは明らかにされた実装の詳細についてのみチェックされるべきだと思います。
X
反復可能なオブジェクトである必要があります。つまり、__iter__()
メソッドが必要です。
したがって、for..in
ループまたはリスト内包表記を開始するには、firstX
の__iter__()
メソッドを呼び出してイテレータオブジェクトを取得します。次に、そのオブジェクトのnext()
メソッドは、が発生するまで反復ごとに呼び出され、StopIteration
発生すると反復が停止します。
3番目の質問の意味と、イテレータが一度にメモリ内のリスト全体を作成するべきではないことを除いて、4番目の質問に意味のある答えを提供する方法がわかりません。
たぶんこれは役に立ちます(チュートリアルhttp://docs.python.org/tutorial/classes.htmlセクション9.9):
舞台裏では、forステートメントがコンテナオブジェクトに対してiter()を呼び出します。この関数は、コンテナ内の要素に一度に1つずつアクセスするメソッドnext()を定義するイテレータオブジェクトを返します。要素がなくなると、next()はStopIteration例外を発生させ、forループを終了するように指示します。
あなたの質問に答えるには:
背後にあるメカニズムはどのように機能しますか?
他の人がすでに述べたように、これは通常のforループで使用されるのとまったく同じメカニズムです。
X内のどの関数/メソッドを呼び出しますか?
以下のコメントに記載されているようにiter(X)
、イテレータを取得するために呼び出します。X
メソッド関数が定義されている場合__iter__()
、これはイテレータを返すために呼び出されます。それ以外の場合、X
を定義すると__getitem__()
、これは繰り返し呼び出されて繰り返されX
ます。こちらのPythonドキュメントを参照してください:http iter()
://docs.python.org/library/functions.html#iter
Xが複数の方法に準拠できる場合、優先順位は何ですか?
ここにあなたの質問が正確に何であるかはわかりませんが、Pythonにはメソッド名を解決する方法に関する標準のルールがあり、それらはここで守られています。これについての議論は次のとおりです。
新しいスタイルのPythonクラスのメソッド解決順序(MRO)
リスト内包表記が速くなるように、効率的なXを作成するにはどうすればよいですか?
Pythonのイテレータとジェネレータについてもっと読むことをお勧めします。クラスサポートの反復を行う簡単な方法の1つは、iter()のジェネレーター関数を作成することです。ジェネレータについての説明は次のとおりです。