range() よりも常に xrange() を優先する必要がありますか?
質問
なぜ、あるいはなぜそうではないのでしょうか?
解決
パフォーマンスの観点から、特に広範囲にわたって反復処理を行う場合、 xrange()
通常はその方が良いです。ただし、場合によっては、その方がよい場合もあります。 range()
:
Python3では、
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 つの違いは、xrange() は C の int より大きい数値をサポートできないことです。そのため、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
Python 3 にはこの問題はありません。
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 でこれをすべて実行する必要があります...
本に載っている良い例: 実践的なPython マグナス・リー・ヘトランド著
>>> zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
前の例ではXrangeの代わりに範囲を使用することはお勧めしません。ただし、最初の5つの数値のみが必要ですが、範囲はすべての数値を計算し、多くの時間がかかる場合があります。xrange を使用すると、必要な数値のみが計算されるため、これは問題になりません。
はい、@Brian の回答を読みました。Python 3 では、range() はジェネレーターであり、xrange() は存在しません。
次の理由により、範囲を選択してください。
1) xrange は、新しい Python バージョンでは廃止されます。これにより、将来の互換性が容易になります。
2) range は xrange に関連する効率を引き継ぎます。
さて、ここにいる皆さんは、xrange と range のトレードオフと利点に関してさまざまな意見を持っています。これらはほとんど正しく、xrange はイテレータであり、range は肉付けされて実際のリストを作成します。ほとんどの場合、この 2 つの違いはほとんどわかりません。(map を range で使用することはできますが、xrange では使用できませんが、より多くのメモリを消費します。)
ただし、皆さんが聞きたいのは、xrange が推奨されるということです。Python 3 の range はイテレータであるため、コード変換ツール 2to3 は、xrange のすべての使用を range に正しく変換し、range の使用に対してエラーまたは警告をスローします。将来コードを簡単に変換したい場合は、xrange のみを使用し、リストが必要であることが確実な場合は list(xrange) を使用します。私はこれを今年 (2008 年) シカゴで開催された PyCon の CPython スプリント中に学びました。
range()
:range(1, 10)
1 から 10 までの数値のリストを返し、リスト全体をメモリに保持します。xrange()
:のようにrange()
, ですが、リストを返す代わりに、オンデマンドで範囲内の数値を生成するオブジェクトを返します。ループの場合、これはよりわずかに高速ですrange()
メモリ効率も向上します。xrange()
オブジェクトをイテレータのように処理し、オンデマンドで数値を生成します (遅延評価)。
In [1]: range(1,10)
Out[1]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
In [2]: xrange(10)
Out[2]: xrange(10)
In [3]: print xrange.__doc__
Out[3]: xrange([start,] stop[, step]) -> xrange object
range()
と同じことをします xrange()
Python 3 で使用されていましたが、用語はありません xrange()
Python 3には存在します。range()
同じシーケンスを複数回繰り返す場合、シナリオによっては実際に高速になる可能性があります。 xrange()
毎回整数オブジェクトを再構築する必要がありますが、 range()
実数の整数オブジェクトを持ちます。
その間 xrange
よりも速いです range
ほとんどの状況では、パフォーマンスの違いはごくわずかです。以下の小さなプログラムは、反復処理を比較します。 range
と xrange
:
import timeit
# Try various list sizes.
for list_len in [1, 10, 100, 1000, 10000, 100000, 1000000]:
# Time doing a range and an xrange.
rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n'%list_len, number=1000)
xrtime = timeit.timeit('a=0;\nfor n in xrange(%d): a += n'%list_len, number=1000)
# Print the result
print "Loop list of len %d: range=%.4f, xrange=%.4f"%(list_len, rtime, xrtime)
以下の結果は次のことを示しています xrange
確かに速いですが、汗を流すほどではありません。
Loop list of len 1: range=0.0003, xrange=0.0003
Loop list of len 10: range=0.0013, xrange=0.0011
Loop list of len 100: range=0.0068, xrange=0.0034
Loop list of len 1000: range=0.0609, xrange=0.0438
Loop list of len 10000: range=0.5527, xrange=0.5266
Loop list of len 100000: range=10.1666, xrange=7.8481
Loop list of len 1000000: range=168.3425, xrange=155.8719
ですので、ぜひご利用ください xrange
, ただし、制約のあるハードウェアを使用している場合を除き、あまり心配する必要はありません。