Python3にxrange関数がないのはなぜですか?


273

最近、私はPython3を使い始めましたが、xrangeに問題はありません。

簡単な例:

1) Python2:

from time import time as t
def count():
  st = t()
  [x for x in xrange(10000000) if x%4 == 0]
  et = t()
  print et-st
count()

2) Python3:

from time import time as t

def xrange(x):

    return iter(range(x))

def count():
    st = t()
    [x for x in xrange(10000000) if x%4 == 0]
    et = t()
    print (et-st)
count()

結果はそれぞれ次のとおりです。

1) 1.53888392448 2) 3.215819835662842

何故ですか?つまり、なぜxrangeが削除されたのですか?学ぶのにとても良いツールです。初心者にとって、私と同じように、私たち全員がある時点でのように。なぜそれを削除するのですか?誰かが適切なPEPを指摘してもらえますか、見つかりません。

乾杯。


231
rangePython 3.xではxrange、Python 2.xからのものです。range削除されたのは、実際にはPython 2.x でした。
Anorov 2013

27
PS、あなたは決して時間をかけるべきではありませんtime。使いやすく、間違えにくく、テストを繰り返すことに加えて、timeit覚えていないすべての種類のこと、または方法を知っていること(GCを無効にするなど)を処理し、数千倍の解像度を持つ時計。
abarnert 2013

7
また、なぜフィルターをかける時間をテストrangeしているのx%4 == 0ですか?なぜlist(xrange())vsをテストするだけでlist(range())はなく、余分な作業ができるだけ少ないのですか?(たとえば、3.xの処理速度が低下していないことをどのようにして確認しますかx%4?)そのために、list大量のメモリ割り当てを伴う巨大なを構築するのはなぜですか(低速であるだけでなく、非常に可変的です)。 ?
abarnert 2013

5
docs.python.org/3.0/whatsnew/3.0.htmlのセクション「リストではなくビューとイテレータ」を参照してください:「range()は、任意のサイズの値で機能することを除いて、以前は動作していたxrange()のように動作します。後者もはや存在しない。" したがって、rangeはイテレータを返します。iter(range)冗長です。
ToolmakerSteve

9
申し訳ありませんが、変更ドキュメントを引用しても、それが盲目的に明白になるわけではありません。混乱していて、長く受け入れられている答えとそのすべてのコメントを読みたくない人のために: python 2でxrangeを使用していたところはどこでも、python 3でrangeを使用します。イテレータを返します。リストの結果が必要な場合は、を実行してくださいlist(range(..))。これはpython 2の範囲と同等です。または別の言い方をすると、xrangeはrangeに名前が変更されました。両方list(range)を用意する必要はありませんでした。本当にリストが必要な場合は必要です。
ToolmakerSteve

回答:


175

いくつかのパフォーマンス測定。を使用timeitして手動で実行する代わりに使用しtimeます。

まず、Apple 2.7.2 64ビット:

In [37]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.05 s per loop

今、python.org 3.3.0 64ビット:

In [83]: %timeit collections.deque((x for x in range(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.32 s per loop

In [84]: %timeit collections.deque((x for x in xrange(10000000) if x%4 == 0), maxlen=0)
1 loops, best of 3: 1.31 s per loop

In [85]: %timeit collections.deque((x for x in iter(range(10000000)) if x%4 == 0), maxlen=0) 
1 loops, best of 3: 1.33 s per loop

どうやら、3.xはrange実際には2.xより少し遅いですxrange。そしてOPのxrange機能はそれとは何の関係もありません。(__iter__スロットへの1回限りの呼び出しは、ループ内で発生したすべての呼び出しに対する10000000の呼び出しの中で表示される可能性は低いので、当然のことですが、誰かがそれを可能性として持ち出しました。)

しかし、それはわずか30%遅くなります。OPはどのように2倍遅くなったのですか?まあ、32ビットPythonで同じテストを繰り返すと、1.58対3.12になります。したがって、これは、3.xが32ビットに害を及ぼすような方法で64ビットパフォーマンス用に最適化された場合のもう1つのケースだと思います。

しかし、それは本当に重要ですか?3.3.0 64ビットでもう一度確認してください:

In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop

したがって、の作成にlistは、全体の反復よりも2倍以上長い時間がかかります。

そして、「Python 2.6+よりもはるかに多くのリソースを消費する」については、私のテストから、3.x rangeは2.x xrangeとまったく同じサイズであるように見えます。さらに、10倍のサイズであっても、不要なリストを作成します。それでも、範囲の反復で何ができるかよりも約10000000x多い問題です。

そして、for内部のCループの代わりに明示的なループはdequeどうですか?

In [87]: def consume(x):
   ....:     for i in x:
   ....:         pass
In [88]: %timeit consume(x for x in range(10000000) if x%4 == 0)
1 loops, best of 3: 1.85 s per loop

そのため、forステートメントを反復する実際の作業と同じくらいの時間をステートメントで無駄にしましたrange

範囲オブジェクトの反復の最適化について心配している場合は、おそらく間違った場所を探しているでしょう。


その間、xrange同じことを何回言われたとしても、なぜ削除されたのかと尋ね続けますが、繰り返します。削除されませんでした。名前がに変更されrange、2.x rangeが削除されました。

3.3 rangeオブジェクトが2.x xrangeオブジェクトの直接の子孫であり(2.x range関数の子孫ではない)ことを証明するいくつかの例を次に示します:3.3rangeおよび2.7のxrangeソース。変更履歴も確認できます(ファイルの任意の場所にある文字列 "xrange"の最後のインスタンスを置き換えた変更にリンクされていると思います)。

では、なぜ遅いのでしょうか?

まあ、一つには、彼らは多くの新機能を追加しました。もう1つの理由として、彼らはあらゆる場所(特に反復の内部)であらゆる種類の変更を行っており、小さな副作用があります。そして、重要度の低いケースをわずかに悲観的にすることさえあるとしても、さまざまな重要なケースを劇的に最適化する多くの作業がありました。これをすべて足し合わせれば、rangeできるだけ速く反復することが少し遅くなったとしても驚くことではありません。これはそれほど重要ではないケースの1つであり、誰も集中するほど十分に気にすることはありません。このパフォーマンスの違いがコード内のホットスポットである実際のユースケースを誰も持つ可能性はほとんどありません。


しかし、それはわずか30%遅くなります。まだ遅いですが、素晴らしい対応相手です。それでも私の質問には答えません:なぜxrangeが削除されたのですか?このように考えてください。一度に消費する必要があるキューの量を知っているマルチプロセッシングに基づくパフォーマンス依存のアプリがある場合、30%が違いを生むでしょうか?ほら、それは重要ではないと言いますが、範囲を使用するたびに、xrangeがそれを行わない一方で、CPUがそれを意味する巨大な悲惨なファンの音が最悪だと聞きます。考えてみてください;)
カタレシア2013

9
@catalesia:再び、削除されずに、単に名前が変更されましたrangerange3.3オブジェクトは、直接の子孫であるxrange2.7で、しないの目的range2.7で機能します。それはのitertools.imapために削除されたときに尋ねるようなものですmap。そのようなことが起こらなかったので、答えはありません。
abarnert 2013

1
@catalesia:パフォーマンスのマイナーな変更は、おそらく範囲を遅くするという直接の設計決定の結果ではなく、Python全体で4年間の変更により、多くのことが速くなり、いくつかが少し遅くなった(そしていくつかのことが起こった)副作用です。 x86_64では速く、x86では遅く、一部のユースケースでは速く、他のケースでは遅くなるなど)。range他に何もせずに反復するのにかかる時間に、どちらの方法でも30%の違いがあることを心配する人はいなかったでしょう。
abarnert 2013

1
他に何もせずに範囲を反復するのにかかる時間について、どちらの方法でも30%の違いを心配する人はいなかったでしょう」まさに。
カタレシア2013

18
@カタレシア:はい、その通りです。しかし、それはそれが言うことの反対を意味すると考えているようです。これは誰もが気にするユースケースではないため、30%遅くなることに誰も気づきませんでした。だから何?このため、Python 3.3では2.7(または2.6)よりも実行速度が遅い実際のプログラムを見つけることができれば、人々は気にかけるでしょう。できない場合はできません。できません。
abarnert 2013

141

Python3の範囲 Python2のxrangeです。iterをラップする必要はありません。Python3で実際のリストを取得するには、次を使用する必要がありますlist(range(...))

Python2とPython3で動作するものが必要な場合は、これを試してください

try:
    xrange
except NameError:
    xrange = range

1
Python 2とPython 3の両方で機能するコードが必要になる場合があります。これは優れたソリューションです。
Greg Glockner 2017年

3
トラブルは両方を使用して、この、コード付きということであるrangexrange異なる動作をします。これを行うだけでは十分ではありません。rangeリストが返されていると決して想定しないようにする必要もあります(Python 2の場合と同様)。
LangeHaare 2017

このプロジェクトからxrangeを使用できます。そこでfuturize自動的にソースコードを変換するツールは:python-future.org/...
guettli

17

Python 3のrangeタイプは、Python 2 のタイプと同じように機能しxrangeます。xrange関数が返すイテレータはrange直接反復した場合に得られるものとまったく同じであるため、スローダウンが発生する理由はわかりません。

システムのスローダウンを再現できません。これが私がテストした方法です:

Python 2とxrange

Python 2.7.3 (default, Apr 10 2012, 23:24:47) [MSC v.1500 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)
18.631936646865853

Python 3 rangeは、少し高速です。

Python 3.3.0 (v3.3.0:bd8afb90ebf2, Sep 29 2012, 10:57:17) [MSC v.1600 64 bit (AMD64)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import timeit
>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)
17.31399508687869

私は最近、Pythonの3のことを学びましたrangeタイプは、このようなスライスのサポートなど、他のいくつかのきちんとした機能を持って:range(10,100,2)[5:25:5]ですrange(20, 60, 10)


おそらく、スローダウンは新しいもののルックアップがxrange何度もあることが原因ですか、それとも1回だけですか
askewchan 2013

とにかく、イテレータは実際に速度を上げますか?メモリを節約しただけだと思いました。
askewchan 2013

3
@catalesia私はここでのポイントは、そのだと思いxrangeましたではないだけで、削除、名前を変更します
askewchan 2013

1
@Blckknght:乾杯ですが、それでも次のような説明はありません。 x、y])。{F(x)for x in S in P(x)}はset(F(x)for x in S if P(x))。NB。{range(x)}はset( [range(x)])、NOT set(range(x))。空のセットにはリテラルがありません。set()(または{1}&{2} :-)を使用してください。frozensetリテラルはありません。めったに必要とされない。 "
カタレシア2013

3
3.x rangeで最大の勝利は、私に関する限り、一定時間__contains__です。初心者が以前書いていて300000 in xrange(1000000)、それが全体xrange(または少なくとも最初の30%)を反復する原因となったため、Pythonのように見えても、なぜそれが悪い考えだったのかを説明する必要がありました。現在、それ pythonicです。
abarnert 2013

1

python2コードを修正する1つの方法は次のとおりです。

import sys

if sys.version_info >= (3, 0):
    def xrange(*args, **kwargs):
        return iter(range(*args, **kwargs))

1
ポイントはpython3にありますxrangeは定義されていないため、xrangeを使用していたレガシーコードは壊れます。
アンドリューパテ2017年

いいえ、range = xrange@ John La Royのコメントにあるように単に定義します
mimi.vx

@ mimi.vx xrangeが定義されていないため、range = xrangeがPython3で機能するかどうかは不明です。私のコメントは、xrange呼び出しを含む古いレガシーコードがあり、それをpython3で実行しようとしているケースに言及しています。
パテ

1
ああ、私の悪い.. xrange = range...ステートメントを切り替えました
mimi.vx

範囲はIS iiterator、それが最初の全範囲を展開しており、この種のもののためのイテレータを使用する利点を失うので、とにかくこれは、それがなかった場合でも、ひどいアイデアだろう。したがって、正しい応答は「range = xrange」ではなく、その「xrange = range」
Shayne

0

Python 2のxrangeはジェネレーターであり、イテレーターを実装していますが、rangeは単なる関数です。Python3では、なぜxrangeから削除されたのかわかりません。


いいえ、範囲はインタレータではありません。この構造ではnext()を実行できません。詳細については、こちらをチェックしてください。treyhunner.com/ 2018/02 / python
Michel Fernandes

説明をありがとうございました。ただし、元のコメントの意図を再range()説明しxrange()ます。つまり、PY3 はPY2 と同等です。したがって、PY3 xrange()では冗長です。
Stephen Rauch

-2

comp:〜$ python Python 2.7.6(デフォルト、Jun 22 2015、17:58:13)[GCC 4.8.2] on linux2

>>> import timeit
>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)

5.656799077987671

>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=100)

5.579368829727173

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

21.54827117919922

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

22.014557123184204

timeit number = 1パラメータ:

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)

0.2245171070098877

>>> timeit.timeit("[x for x in xrange(1000000) if x%4]",number=1)

0.10750913619995117

comp:〜$ python3 Python 3.4.3(デフォルト、2015年10月14日20:28:29)[GCC 4.8.4] Linux版

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

9.113872020003328

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=100)

9.07014398300089

timeit number = 1,2,3,4 paramを使用すると、paramは迅速かつ直線的に機能します。

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=1)

0.09329321900440846

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=2)

0.18501482300052885

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=3)

0.2703447980020428

>>> timeit.timeit("[x for x in range(1000000) if x%4]",number=4)

0.36209142999723554

したがって、timeit.timeit( "[x for x in range(1000000)if x%4]"、number = 1)のような1つの実行ループサイクルを測定すると、Python3は十分に速く動作します。しかし、繰り返しループでは、python 2 xrange()は、python 3のrange()に対して速度で勝ちます。


しかし、これは言語自体によるものです... xrange / rangeとは関係ありません。
mimi.vx
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.