29

私は、反復可能なもので一致する要素を数えるほとんどのpythonic方法からこのコードに遭遇しました

r = xrange(1, 10)
print sum(1 for v in r if v % 2 == 0) # 4
print sum(1 for v in r if v % 3 == 0) # 3

rは1回繰り返されます。そして、それは再び繰り返されます。イテレータが一度消費されたら、それは終わったので、もう一度繰り返すべきではないと思いました。

ジェネレータ式は1回だけ繰り返すことができます。

r = (7 * i for i in xrange(1, 10))
print sum(1 for v in r if v % 2 == 0) # 4
print sum(1 for v in r if v % 3 == 0) # 0

enumerate(L)も:

r = enumerate(mylist)

およびファイルオブジェクトも:

f = open(myfilename, 'r')

xrangeの動作が異なるのはなぜですか?

4

3 に答える 3

38

xrangeジェネレータを返さないため。xrangeオブジェクトを返します。

>>> type(xrange(10))
<type 'xrange'>

繰り返される反復に加えて、xrangeオブジェクトは、ジェネレーターがサポートしていない他の機能をサポートします。たとえば、インデックス付けなどです。

>>> xrange(10)[5]
5

それらはまた長さを持っています:

>>> len(xrange(10))
10

そして、それらは逆にすることができます:

>>> list(reversed(xrange(10)))
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

つまり、xrangeオブジェクトは完全なシーケンス インターフェイスを実装します。

>>> import collections
>>> isinstance(xrange(10), collections.Sequence)
True

彼らは多くのメモリを消費することなくそれを行うだけです。

rangePython 3では、によって返されるオブジェクトrangeがすべて同じプロパティを持っていることにも注意してください。

于 2012-05-27T18:31:16.487 に答える
17

xrange呼び出しによって生成されたオブジェクトは、繰り返されるたびにそれ自体の一意のバージョン(実際には個別のオブジェクト)を提供するをxrange()指定するためです。__iter__rangeiterator

>>> x = xrange(3)
>>> type(x)
<type 'xrange'>
>>> i = x.__iter__()
>>> type(i)
<type 'rangeiterator'>
于 2012-05-27T18:29:41.043 に答える
2

何かについて知っているのはそれがイテレータであるということだけである場合、一般に、それを繰り返すことができるのは1回だけであると想定する必要があります。これは、すべてのイテレータを1回だけ消費できるという意味ではなく、すべてのイテレータを少なくとも1回は消費できるということです。明らかな例は、リストやその他のシーケンスがこのインターフェイスをサポートしていることです。

senderleとAmberが説明したように、呼び出しによって取得する特定のイテレーターはxrange、それらを複数回繰り返すことができるように実装されています。

一般的なイテレータの考え方では、イテレータが繰り返された後に使い果たされる可能性があります。これは、多くのイテレータ(ジェネレータ、ファイルトラバーサルなど)が任意に多くのトラバーサルをサポートする必要がある場合、実装が困難であるか、はるかに多くのメモリを消費するか、実行速度が大幅に低下するためです。使用済み。したがって、イテレータが任意に多くのトラバーサルをサポートする必要がある場合、これらのものはおそらくイテレータではありません。

簡単に言うと、任意の未知のイテレータで動作するコードを記述している場合、トラバースできるのは1回だけであり、必要以上の機能をサポートするオブジェクトを誰かが提供してもかまいませ。イテレータに関する追加情報(シーケンスでもある、xrangeオブジェクトであるなど)がわかっている場合は、必要に応じてそれを利用するようにコーディングできます。

于 2012-06-29T01:43:12.157 に答える