常にrange()よりもxrange()を優先する必要がありますか?


460

なぜか、なぜそうでないのか?


36
誰かが私たちのPython以外の人のための2つの違いを簡単に説明できますか?たぶん「xrange()はrangeh)のすべてを行いますが、X、Y、およびZもサポートします」
Outlaw Programmer

87
range(n)は、すべての整数0..n-1を含むリストを作成します。これはrange(1000000)を実行すると問題になります。最終的には4Mbを超えるリストになるためです。xrangeは、リストのふりをするオブジェクトを返すことでこれに対処しますが、要求されたインデックスから必要な数を計算し、それを返します。
ブライアン


4
基本的に、一方range(1000)listxrange(1000)のように作用するオブジェクトであるgenerator(それは確かにあるがないもの)。また、xrangeより高速です。あなたがすることができimport timeit from timeit、その後、ちょうど持っていることの方法を作るfor i in xrange: passために、別のrange、そして行うtimeit(method1)timeit(method2)と、驚くなかれ、xrangeのは、(あなたがリストを必要としないときである)時には、ほぼ倍の速さです。(私にとっては、i in xrange(1000):passvsはi in range(1000):passとった13.316725969314575vs 21.190124988555908秒のそれぞれ-それはたくさんあります。)
dylnmc

別のパフォーマンステストでxrange(100)、よりも20%速くなっていrange(100)ます。
Evgeni Sergeev

回答:


443

パフォーマンスについては、特に大きな範囲を反復する場合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()でラップされている場合、範囲は変更されません。


5
「範囲はイテレータになる」とはどういう意味ですか?これは「ジェネレータ」ではありませんか?
Michael Mior

4
いいえ。ジェネレーターは特定のタイプのイテレーターを指し、新しいものrangeはとにかくイテレーターではありません。
user2357112は

2番目の弾丸は実際には意味がありません。オブジェクトを複数回使用することはできないと言っています。できますよ!xr = xrange(1,11)次の行で試してみてくださいfor i in xr: print " ".join(format(i*j,"3d") for j in xr)!あなたはあなたのタイムテーブルを10まで持っています。それはちょうどと同じように機能r = range(1,11)してfor i in r: print " ".join(format(i*j,"3d") for j in r)...すべてがPython2内のオブジェクトです。あなたが言うつもりだったのは、インデックスベースの理解が(それが理にかなっている場合は)とrangeは対照的により上手にできるということxrangeです。範囲が便利になることはめったにないと思います
dylnmc 14年

(足りません部屋)が、私はないことだと思うrangeあなたが使用したい場合は便利なことができlistたループにし、そのリストにある特定の条件やアペンドのものに基づいて、特定のインデックスを変更し、range間違いなく良いです。ただし、xrange単に高速でメモリ使用量も少ないため、forループアプリケーションの大部分では、それが最適であるように見えます。質問者の質問に戻ると、ほとんどありませんが存在するインスタンスのrange方が良いでしょう。おそらく私が考えているほどめったにないが、私は確かxrangeに時間の95%を使用します。
dylnmc 2014年

129

いいえ、どちらにも用途があります。

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

42

あなたは賛成すべきであるrange()以上xrange()、あなたが実際のリストを必要とする場合のみです。たとえば、から返されたリストを変更したい場合range()、またはスライスしたい場合。反復の場合、または通常のインデックス作成の場合でも、正常xrange()に動作します(通常ははるかに効率的です)。非常に小さいリストrange()よりも少し速い点がありますが、xrange()ハードウェアやその他のさまざまな詳細によっては、損益分岐が長さ1または2の結果になる場合があります。心配する必要はありません。好むxrange()


30

もう1つの違いは、xrange()がCの整数より大きい数値をサポートできないことです。そのため、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)

13

xrange()オブジェクトのリストを生成する代わりに、一度に1つのオブジェクトを生成するだけなので、より効率的です。100の整数とそのオーバーヘッド、およびそれらを配置するためのリストの代わりに、一度に1つの整数しかありません。より高速な生成、より良いメモリ使用、より効率的なコード。

特に何かのリストが必要でない限り、私は常に支持します xrange()


8

range()はリストを返し、xrange()はxrangeオブジェクトを返します。

xrange()は少し高速で、メモリ効率も少し向上しています。しかし、利益はそれほど大きくありません。

リストが使用する余分なメモリはもちろん無駄になるだけでなく、リストにはより多くの機能(スライス、繰り返し、挿入など)があります。正確な違いはドキュメントにあります。難しいルールはありません。必要なものを使用してください。

Python 3.0はまだ開発中ですが、IIRC range()は2.Xのxrange()と非常によく似ており、list(range())を使用してリストを生成できます。


5

スライス機能とインデックス機能を備えた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はとにかくこれをすべて実行する必要があります...


そうだね。まったく異なるテクノロジーから、lodashでの作業を確認して遅延させます:github.com/lodash/lodash/issues/274。スライスなどは可能な限り怠惰である必要があり、そうでない場合は具体化する必要があります。
Rob Grant

4

本で与えられた良い例:マグヌス・リー・ヘットランド著の実用的なPython

>>> zip(range(5), xrange(100000000))
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]

前の例では、xrangeの代わりにrangeを使用することはお勧めしません。最初の5つの数値のみが必要ですが、rangeはすべての数値を計算するため、時間がかかる場合があります。xrangeでは、必要な数値のみを計算するため、これは問題にはなりません。

はい、私は@Brianの答えを読みました。Python3では、range()はとにかくジェネレーターであり、xrange()は存在しません。


3

これらの理由のために範囲で行く:

1)xrangeは、新しいバージョンのPythonでは廃止されます。これにより、将来の互換性が容易になります。

2)rangeはxrangeに関連する効率を引き受けます。


13
これを行わないでください。xrange()はなくなりますが、他の多くのものも消えます。Python 2.xコードをPython 3.xコードに変換するために使用するツールは、xrange()をrange()に自動的に変換しますが、range()は効率の悪いlist(range())に変換されます。
Thomas Wouters

10
トーマス:実はそれより少し賢いです。実際のリスト(forループや内包など)が明らかに必要ない状況では、range()を単純なrange()に変換します。変数に割り当てられるか、直接使用される場合のみ、list()でラップする必要があります
Brian

2

さて、ここにいる全員が、xrangeとrangeのトレードオフと利点について異なる意見を持っています。それらはほとんど正しいです、xrangeはイテレータであり、範囲は実際にリストを作成して作成します。ほとんどの場合、2つの違いに気付くことはありません。(xrangeではなくrangeでmapを使用できますが、より多くのメモリを消費します。)

しかし、集会で聞きたいのは、xrangeの方が好ましいということです。Python 3のrangeはイテレーターであるため、コード変換ツール2to3はxrangeのすべての使用を正しくrangeに変換し、rangeの使用に対してエラーまたは警告をスローします。将来的に簡単にコードを確実に変換したい場合は、xrangeのみを使用し、リストが確実に必要な場合はlist(xrange)を使用します。私はこれを、今年(2008年)シカゴのPyConで行われたCPythonスプリント中に学びました。


8
それは真実ではない。「for x in range(20)」のようなコードは範囲として残され、「x = range(20)」のようなコードは「x = list(range(20))」に変換されます-エラーはありません。さらに、2.6と3.0の両方で実行されるコードを記述したい場合、range()が互換性関数を追加しない唯一のオプションです。
ブライアン

2
  • 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で使用するのと同じことを行いますが、Python 3には用語xrange()が存在し ませんrange()。同じシーケンスを複数回繰り返すと、一部のシナリオでは実際に高速になる場合があります。xrange()整数オブジェクトを毎回再構築range()する必要がありますが、実際の整数オブジェクトがあります。


2

一方でxrangeより速いですrangeほとんどの状況では、パフォーマンスの違いはかなり最小限です。以下の小さなプログラムは、a rangeとanの反復を比較しています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ください。ただし、制約のあるハードウェアを使用しているのでない限り、あまり気にしないでください。


あなたがlist_len使用されていないので、あなただけの長さが100のリストにこのコードを実行している
マーク・

私は、リストの長さを変更し、実際にお勧めします:rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000)
マーク・

うわー、今週は良い週になりそうです。おかげで、修正されました。それでも、大きな違いはありません。
speedplane 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.