なぜですか、そうでないのですか?
12 に答える
パフォーマンスに関しては、特に広い範囲で繰り返し処理を行う場合xrange()
は通常より優れています。しかし、あなたが好むかもしれないいくつかのケースがまだありますrange()
:
Python 3 では、以前
range()
は行っていたものxrange()
を実行し、xrange()
存在しません。Python 2 と Python 3 の両方で実行されるコードを記述したい場合は、xrange()
.range()
場合によっては、実際にはより高速になる可能性があります。同じシーケンスを複数回繰り返す場合。xrange()
毎回整数オブジェクトを再構築する必要がrange()
ありますが、実際の整数オブジェクトがあります。(ただし、メモリの点では常にパフォーマンスが低下します)xrange()
実際のリストが必要なすべての場合に使用できるわけではありません。たとえば、スライスやリスト メソッドはサポートされていません。
range()
[編集] 2to3 ツールによってどのようにアップグレードされるかについて言及している投稿がいくつかあります。range()
記録として、との使用例でツールを実行した場合の出力を次に示します。xrange()
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: ws_comma
--- range_test.py (original)
+++ range_test.py (refactored)
@@ -1,7 +1,7 @@
for x in range(20):
- a=range(20)
+ a=list(range(20))
b=list(range(20))
c=[x for x in range(20)]
d=(x for x in range(20))
- e=xrange(20)
+ e=range(20)
ご覧のとおり、for ループまたは内包表記で使用される場合、または list() で既にラップされている場合、範囲は変更されません。
いいえ、どちらにも用途があります。
xrange()
メモリを節約するため、反復するときに使用します。言う:
for x in xrange(1, one_zillion):
それよりも:
for x in range(1, one_zillion):
一方、range()
実際に数字のリストが必要な場合はを使用します。
multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven
実際のリストが必要な場合にのみ優先range()
する必要があります。xrange()
たとえば、 によって返されたリストを変更したい場合range()
や、スライスしたい場合です。反復または通常のインデックス作成でも、正常xrange()
に機能します (通常ははるかに効率的です)。range()
非常に小さなリストよりも少し速い点がありxrange()
ますが、ハードウェアやその他のさまざまな詳細に応じて、損益分岐点は長さ 1 または 2 の結果になる可能性があります。心配する必要はありません。優先しxrange()
ます。
もう1つの違いは、Python2の実装はxrange()
Cintより大きい数をサポートできないため、Pythonに組み込まれている多数のサポートを使用して範囲を設定する場合は、を使用する必要がありますrange()
。
Python 2.7.3 (default, Jul 13 2012, 22:29:01)
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
[123456787676676767676676L, 123456787676676767676677L, 123456787676676767676678L]
>>> xrange(123456787676676767676676,123456787676676767676679)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OverflowError: Python int too large to convert to C long
Python3にはこの問題はありません。
Python 3.2.3 (default, Jul 14 2012, 01:01:48)
[GCC 4.7.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> range(123456787676676767676676,123456787676676767676679)
range(123456787676676767676676, 123456787676676767676679)
xrange()
オブジェクトのリストを生成する代わりに、一度に 1 つのオブジェクトを生成するだけなので、より効率的です。100 個の整数、すべてのオーバーヘッド、およびそれらを入れるリストの代わりに、一度に 1 つの整数しかありません。生成の高速化、メモリの使用効率の向上、コードの効率化。
何かのリストが特に必要でない限り、私はいつも好きですxrange()
range() はリストを返し、xrange() は xrange オブジェクトを返します。
xrange() は少し高速で、メモリ効率も少し高くなります。しかし、ゲインはそれほど大きくありません。
もちろん、リストによって使用される余分なメモリは無駄になるだけではなく、リストにはより多くの機能 (スライス、繰り返し、挿入など) があります。正確な違いはドキュメントにあります。骨の折れるルールはありません。必要なものを使用してください。
Python 3.0 はまだ開発中ですが、IIRC range() は 2.X の xrange() に非常に似ており、list(range()) を使用してリストを生成できます。
スライスとインデックス機能を備えた xrange オブジェクトを取得するのはそれほど難しくありません。私は、非常にうまく機能し、カウント(反復)時に xrange と同じくらい高速なコードをいくつか作成しました。
from __future__ import division
def read_xrange(xrange_object):
# returns the xrange object's start, stop, and step
start = xrange_object[0]
if len(xrange_object) > 1:
step = xrange_object[1] - xrange_object[0]
else:
step = 1
stop = xrange_object[-1] + step
return start, stop, step
class Xrange(object):
''' creates an xrange-like object that supports slicing and indexing.
ex: a = Xrange(20)
a.index(10)
will work
Also a[:5]
will return another Xrange object with the specified attributes
Also allows for the conversion from an existing xrange object
'''
def __init__(self, *inputs):
# allow inputs of xrange objects
if len(inputs) == 1:
test, = inputs
if type(test) == xrange:
self.xrange = test
self.start, self.stop, self.step = read_xrange(test)
return
# or create one from start, stop, step
self.start, self.step = 0, None
if len(inputs) == 1:
self.stop, = inputs
elif len(inputs) == 2:
self.start, self.stop = inputs
elif len(inputs) == 3:
self.start, self.stop, self.step = inputs
else:
raise ValueError(inputs)
self.xrange = xrange(self.start, self.stop, self.step)
def __iter__(self):
return iter(self.xrange)
def __getitem__(self, item):
if type(item) is int:
if item < 0:
item += len(self)
return self.xrange[item]
if type(item) is slice:
# get the indexes, and then convert to the number
start, stop, step = item.start, item.stop, item.step
start = start if start != None else 0 # convert start = None to start = 0
if start < 0:
start += start
start = self[start]
if start < 0: raise IndexError(item)
step = (self.step if self.step != None else 1) * (step if step != None else 1)
stop = stop if stop is not None else self.xrange[-1]
if stop < 0:
stop += stop
stop = self[stop]
stop = stop
if stop > self.stop:
raise IndexError
if start < self.start:
raise IndexError
return Xrange(start, stop, step)
def index(self, value):
error = ValueError('object.index({0}): {0} not in object'.format(value))
index = (value - self.start)/self.step
if index % 1 != 0:
raise error
index = int(index)
try:
self.xrange[index]
except (IndexError, TypeError):
raise error
return index
def __len__(self):
return len(self.xrange)
正直なところ、この問題全体はばかげていると思いますが、とにかく xrange がこれをすべて行う必要があります...
次の理由により、範囲を使用します。
1) xrange は、新しい Python バージョンでは廃止されます。これにより、将来の互換性が容易になります。
2) range は、xrange に関連する効率を引き継ぎます。
本で与えられた良い例:Practical Python By Magnus Lie Hetland
>>> zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
前の例では、xrange の代わりに range を使用することはお勧めしません。必要なのは最初の 5 つの数値だけですが、range はすべての数値を計算するため、かなりの時間がかかる場合があります。xrange では、必要な数値のみを計算するため、これは問題になりません。
はい、@Brian の回答を読みました: Python 3 では、とにかく range() はジェネレーターであり、xrange() は存在しません。
さて、xrange と range のトレードオフと利点については、ここにいる全員が異なる意見を持っています。それらはおおむね正しく、xrange は反復子であり、range は具体化して実際のリストを作成します。ほとんどの場合、2 つの違いに気付くことはありません。( map は range で使用できますが、xrange では使用できませんが、より多くのメモリを使用します。)
しかし、あなたが聞きたいと思っているのは、xrange が好ましい選択であるということです。Python 3 の range は反復子であるため、コード変換ツール 2to3 は xrange のすべての使用を range に正しく変換し、range の使用に対してエラーまたは警告をスローします。将来コードを簡単に変換したい場合は、xrange のみを使用し、リストが必要な場合は list(xrange) を使用します。これは、今年 (2008 年) にシカゴで開催された PyCon での CPython スプリントで学びました。