最近私はPython 3を使い始めました、そしてそれは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を教えてもらえますか、私はそれを見つけることができません。
乾杯。
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倍の大きさで、不必要なリストを作成することは、範囲の反復で可能なことよりも約10000000倍多く問題があります。
また、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 range
および 2.7 xrange
へのソース 。 変更履歴 (ファイル内の任意の場所にある文字列 "xrange"の最後のインスタンスを置き換える変更にリンクされていると信じています)も見ることができます。
それで、なぜ遅いのですか?
まあ、1つには、多くの新機能が追加されています。別の方法として、彼らはあらゆる面で(特に繰り返しの内部で)軽微な副作用のあるすべての種類の変更を行いました。また、重要度の低いケースをわずかに悲観する場合もありますが、さまざまな重要なケースを劇的に最適化するための多くの作業がありました。これをすべて合計すると、range
を可能な限り高速に反復するのが少し遅くなったのは驚くことではありません。誰も焦点を当てるほど気にしないという、それほど重要ではないケースの1つです。このパフォーマンスの違いがコードのホットスポットであるような実際のユースケースを持っている人はいないでしょう。
Python3の範囲 は Python2のxrangeです。その周りにiterをラップする必要はありません。 Python3で実際のリストを取得するには、list(range(...))
を使う必要があります。
Python 2とPython 3で動作するものが欲しいなら、これを試してください。
try:
xrange
except NameError:
xrange = range
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
range
を使ったPython 3は少し速いです。
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(15, 60, 10)
です!
Python2コードを修正する1つの方法は次のとおりです。
import sys
if sys.version_info >= (3, 0):
def xrange(*args, **kwargs):
return iter(range(*args, **kwargs))
python 2のxrangeはジェネレータであり、iteratorを実装していますが、rangeは単なる関数です。 Python 3では、なぜxrangeが削除されたのかわかりません。