何故ですか?
パフォーマンスを重視する場合、特に広範囲にわたって反復処理を行う場合は、通常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()でラップされている場合には、rangeは変更されません。
いいえ、どちらにも用途があります。
メモリを節約するため、反復するときは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 著Magnus Lie Hetland
>>> Zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
上記の例では、xrangeの代わりにrangeを使用することをお勧めしません。最初の5つの数字だけが必要ですが、rangeはすべての数字を計算するため、時間がかかる場合があります。 xrangeでは、これは問題にはなりません。必要な数だけが計算されるからです。
はい、私は@ Brianの答えを読みました。Python3では、range()はとにかくジェネレータであり、xrange()は存在しません。
これらの理由のために範囲と行きなさい:
1)xrangeは新しいPythonバージョンではなくなるでしょう。これにより、将来の互換性が容易になります。
2)rangeはxrangeに関連した効率を引き継ぎます。
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
を使用してください。ただし、ハードウェアの制約がない限り、あまり心配しないでください。
さて、ここに誰もが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()
は、Python 3で使用されていたxrange()
と同じ動作をします。Python3にはxrange()
という用語はありません。同じシーケンスを複数回繰り返すと、実際にはrange()
のほうが速くなります。 xrange()
は毎回整数オブジェクトを再構築する必要がありますが、range()
は実数の整数オブジェクトを持ちます。