Python3'te neden xrange fonksiyonu yok?


273

Son zamanlarda Python3 kullanmaya başladım ve xrange acıları yok.

Basit bir örnek:

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()

Sonuçlar sırasıyla:

1) 1.53888392448 2) 3.215819835662842

Neden? Yani, xrange neden kaldırıldı? Öğrenmek için harika bir araç. Yeni başlayanlar için, tıpkı benim gibi, hepimizin bir noktada olduğu gibi. Neden kaldırmalıyım? Birisi beni uygun PEP'e yönlendirebilir mi, bulamıyorum.

Şerefe.


231
rangePython 3.x xrangePython 2.x geliyor. Aslında Python 2.x rangekaldırıldı.
Anorov

27
PS, asla zaman geçirmemelisin time. Kullanımı daha kolay ve yanlış yapmak daha zor olmanın yanı sıra testleri sizin için tekrarlamanın yanı sıra, timeithatırlamayacağınız veya hatta nasıl bakacağınızı (GC'yi devre dışı bırakmak gibi) bilmeyeceğiniz her türlü şeyle ilgilenir ve binlerce kat daha iyi çözünürlük.
abarnert

7
Ayrıca, neden filtrelemek için zaman test ediyoruz rangeüzerinde x%4 == 0? Neden sadece teste list(xrange())karşı değil list(range()), mümkün olduğunca az yabancı çalışma var? (Örneğin, 3.x'in x%4daha yavaş yapmadığını nasıl anlarsınız ?) Bu nedenle, neden listçok fazla bellek ayırmayı içeren devasa bir yapı oluşturuyorsunuz (yavaş olmanın yanı sıra inanılmaz derecede değişken) ?
abarnert

5
Bkz. Docs.python.org/3.0/whatsnew/3.0.html , "Listeler Yerine Görünümler ve Yineleyiciler" bölümü: "range () artık alışılmadık boyut değerleriyle çalışması dışında xrange () gibi davranıyor. artık yok." Yani, aralık şimdi bir yineleyici döndürüyor. iter(range)gereksizdir.
ToolmakerSteve

9
Maalesef değişiklik belgesinden alıntı yapıldığında körü körüne açıkça görülmüyor. Kafası karışmışlist(range(..)) ve uzun süredir kabul edilen cevabı ve tüm yorumlarını okumak istemeyen herkes için: Python 2'de xrange'i kullandığınız her yerde, python 3'te aralığı kullanın. bir yineleyici döndürmek. Listedeki sonuçlara ihtiyacınız varsa, yapın . Bu, python 2'nin aralığına eşdeğerdir. Ya da başka bir şekilde söylemek gerekirse: xrange aralık olarak yeniden adlandırıldı, çünkü daha iyi varsayılan değerdir; list(range)gerçekten bir listeye ihtiyacınız varsa , her ikisine sahip olmak gerekli değildi . .
ToolmakerSteve

Yanıtlar:


175

Bazı performans ölçümleri timeitile manuel olarak yapmaya çalışmak yerine time.

İlk olarak, Apple 2.7.2 64 bit:

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

Şimdi, python.org 3.3.0 64-bit:

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

Görünüşe göre, 3.x rangegerçekten 2.x'ten biraz daha yavaş xrange. Ve OP'nin xrangeişlevinin bununla hiçbir ilgisi yoktur. (Bölgeye bir kerelik bir çağrı __iter__, döngüde ne olursa olsun 10000000 çağrı arasında görünmeyeceği için şaşırtıcı değil , ancak biri bir olasılık olarak ortaya çıkardı.)

Ama sadece% 30 daha yavaş. OP nasıl 2 kat yavaşladı? Aynı testleri 32 bit Python ile tekrarlarsam, 1.58'e karşı 3.12 elde ederim. Tahminimce bu, 3.x'in 32-bit'e zarar veren şekillerde 64-bit performans için optimize edildiği durumlardan biri.

Ama gerçekten önemli mi? 3.3.0 64 bit ile tekrar kontrol edin:

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

Yani, binaları oluşturmak listtüm yinelemeden iki kat daha uzun sürer.

Ve "Python 2.6+ 'dan çok daha fazla kaynak tüketiyor" ise, testlerimden, 3.x range, 2.x ile tam olarak aynı boyutta görünüyor xrange- ve 10x kadar büyük olsa bile, gereksiz listeyi oluşturuyor hala yineleme aralığının yapabileceği her şeyden yaklaşık 10000000x daha fazla sorundur.

forİçindeki C döngüsü yerine açık bir döngü ne olacak 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

Yani, forifadenin gerçek çalışmasında olduğu gibi , ifadede neredeyse zaman kaybı oldu range.

Bir aralık nesnesinin yinelemesini optimize etmekten endişe ediyorsanız, muhtemelen yanlış yere bakıyorsunuzdur.


Bu arada, xrangeinsanlar size aynı şeyi kaç kez söylese de, neden kaldırıldığını sormaya devam edersiniz, ancak tekrarlayacağım: Kaldırılmadı: yeniden adlandırıldı rangeve rangekaldırılan şey 2.x.

3.3 rangenesnesinin 2.x xrangenesnesinin (2.x rangeişlevinin değil) doğrudan bir torunu olduğunu gösteren bazı kanıtlar vardır : kaynak 3.3range ve 2.7'yexrange . Değişiklik geçmişini bile görebilirsiniz (inanıyorum ki, dosyanın herhangi bir yerinde "xrange" dizesinin son örneğini değiştiren değişiklikle bağlantılı).

Peki, neden daha yavaş?

Birincisi, birçok yeni özellik eklediler. Bir diğeri için, her yerde (özellikle yineleme içinde) küçük yan etkileri olan her türlü değişikliği yaptılar. Ve bazen, daha az önemli vakaları hafifçe kötüleştirse bile, çeşitli önemli vakaları önemli ölçüde optimize etmek için çok fazla çalışma vardı. Tüm bunları ekleyin ve rangemümkün olduğunca hızlı bir şekilde yinelemenin artık biraz daha yavaş olduğuna şaşırmıyorum . Kimsenin odaklanmak için yeterince umursamadığı daha az önemli olan durumlardan biridir. Hiç kimse, bu performans farkının kodlarındaki sıcak nokta olduğu gerçek hayattaki bir kullanım durumuna sahip olmayacaktır.


Ama sadece% 30 daha yavaş. Hala daha yavaş, ama harika bir yanıt arkadaşı, düşünülmesi gereken bir şey. Ama benim sorularımı cevaplamıyor: xrange neden kaldırıldı ?? Bu şekilde düşünün - bir seferde ne kadar kuyruk kullanmanız gerektiğini bilerek çok işlemciliğe dayalı performansa bağlı bir uygulamanız varsa,% 30 fark yaratır mı, olmaz mı? Görüyorsunuz, bunun önemli olmadığını söylüyorsunuz, ancak menzili her kullandığımda, xrange'in bunu yapmadığı halde, devasa fan sesi cpu'nun anlamı olduğunu duyuyorum. Bunu düşünün;)
catalesia

9
@catalesia: Bir kez daha kaldırılmadı, sadece yeniden adlandırıldı range. range3.3 nesne arasında doğrudan bir soyundan xrangeolmayan bölgesinin 2.7'de nesne range2.7'de fonksiyonu. Bu itertools.imap, lehine çıkarılırken sormak gibi map. Cevap yok, çünkü böyle bir şey olmadı.
abarnert

1
@catalesia: Küçük performans değişiklikleri muhtemelen aralıkları yavaşlatmak için doğrudan bir tasarım kararının sonucu değil, Python'un her yerinde birçok şeyi daha hızlı hale getiren, bazı şeyleri biraz daha yavaş yapan bazı değişikliklerin (ve bazı şeylerin) bir yan etkisidir. x86_64'te daha hızlı, ancak x86'da daha yavaş veya bazı kullanım durumlarında daha hızlı ancak diğerlerinde daha yavaş vb.). Hiç kimse, rangebaşka bir şey yapmadan bir süre yinelemenin ne kadar sürdüğü konusunda% 30 farktan endişe etmiyordu.
abarnert

1
"Hiç kimse, başka bir şey yapmadan bir aralığı yinelemenin ne kadar sürdüğü konusunda% 30'luk bir farktan endişe etmiyordu. "
catalesia

18
@catalesia: Evet, kesinlikle. Ama bunun söylediklerinin tersi anlamına geldiğini düşünüyor gibisin. Hiç kimsenin umursamadığı bir kullanım örneği değil, bu yüzden kimse% 30 daha yavaş olduğunu fark etmedi. Ne olmuş yani? Bu nedenle Python 3.3'te 2.7 (veya 2.6) 'dan daha yavaş çalışan bir gerçek yaşam programı bulabilirseniz, insanlar ilgilenecektir. Eğer yapamazsan, yapmazlar ve yapmamalısın.
abarnert

141

Python3 en aralığı olan Python2 en xrange. Etrafına bir iter sarmaya gerek yok. Python3'te gerçek bir liste almak için şunu kullanmanız gerekir:list(range(...))

Python2 ve Python3 ile çalışan bir şey istiyorsanız, bunu deneyin

try:
    xrange
except NameError:
    xrange = range

1
Bazen hem Python 2 hem de 3'te çalışan koda ihtiyacınız olabilir. Bu iyi bir çözümdür.
Greg Glockner

3
Sorun şu ki, her ikisini de kullanan rangeve xrangefarklı davranacak kod . Bunu yapmak yeterli değildir, bir kişinin rangebir listeyi döndürdüğünü asla varsaymamak da gerekir (python 2'de olduğu gibi).
LangeHaare

Bu projeden xrange kullanabilirsiniz. futurizeKaynak kodunu otomatik olarak dönüştürmek için bir araç var : python-future.org/…
guettli

17

Python 3'ün rangetürü tıpkı Python 2'ler gibi çalışır xrange. Neden yavaşlama gördüğünüzden emin değilim, çünkü xrangeişleviniz tarafından döndürülen yineleyici , rangedoğrudan yinelediğinizde alacağınız şeydir .

Sistemimdeki yavaşlamayı yeniden üretemiyorum. İşte nasıl test ettim:

Karşılaştırma Python 2, şununla 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, rangebiraz daha hızlı:

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

Son zamanlarda Python 3'ün rangetipinin dilimleme desteği gibi başka düzgün özelliklere sahip olduğunu öğrendim : range(10,100,2)[5:25:5]is range(20, 60, 10)!


Belki de yavaşlama, yeninin xrangepek çok kez aranmasından kaynaklanıyor , yoksa bu sadece bir kez mi yapılıyor?
askewchan

Yineleyici aslında hızı zaten artırıyor mu? Hafızayı kurtardığını düşündüm.
askewchan

3
@catalesia Burada nokta olduğunu düşünüyorum xrangeoldu değil kaldırılır sadece adını .
askewchan

1
@Blckknght: Şerefe, ama yine de aşağıdaki gibi bir açıklama yapmayı berbat ediyor: "Değişmez değerleri ve kavrayışları ayarla [19] [20] [bitti] {x} set ([x]) anlamına gelir; {x, y} set ([ {P (x)} ayarlanmışsa x için x için F (x)} (P (x)} ise S için x için (F (x)) anlamına gelir. [aralık (x)]), (aralık (x)) AYARLANMADIĞINDA boş grubu için hazır olduğunu;..; kullanımı grubu () (ya da {1} ve {2} :-) bir frozenset hazır bulunmaktadır onlar da, nadiren gerekli. "
catalesia

3
3.x'teki en büyük galibiyet range, bana göre, sabit süredir __contains__. Yeni yazarlar eskiden yazıyordu 300000 in xrange(1000000)ve bu da bütünün xrange(veya en azından ilk% 30'unun) tekrarlanmasına neden oldu , bu yüzden bu kadar pitonik görünse de bunun neden kötü bir fikir olduğunu açıklamak zorunda kaldık. Şimdi, bu ise pythonic.
abarnert

1

Python2 kodunuzu düzeltmenin bir yolu:

import sys

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

1
Nokta python3'te xrange tanımlanmamıştır, bu nedenle xrange kullanılan eski kod kırılır.
andrew pate

hayır, basitçe range = xrange@John La Roy tarafından yorumda olduğu gibi tanımla
mimi.vx

@ mimi.vx xrange tanımlanmadığı için range = xrange'in Python3'te çalışacağından emin değilim. Benim yorumum, xrange çağrıları içeren eski bir eski koda sahip olduğunuz ve python3 altında çalıştırmaya çalıştığınız durumu ifade eder.
andrew pate

1
Ah, benim kötü .. xrange = range... ifadeleri değiştirdim
mimi.vx

Range bir iiterator IS ve bu olmasa bile bu korkunç bir fikir olurdu, çünkü önce tüm aralığı açmak ve bu tür şeyler için bir yineleyici kullanmanın avantajlarını kaybediyor. Yani doğru cevap "range = xrange" değil "xrange = range"
Shayne

0

Python 2'den xrange bir jeneratördür ve menzil sadece bir işlevken yineleyiciyi uygular. Python3'te neden xrange'den atıldığını bilmiyorum.


Hayır, aralık bir tam sayı değil. Bu yapı ile bir sonraki () işlemi yapamazsınız. Daha fazla bilgi için treyhunner.com/2018/02/python-range-is-not-an-iterator
Michel Fernandes

Açıklama için çok teşekkür ederim. Ama orijinal yorumun niyetini tekrar söyleyeceğim ve bu da PY3'ün PY2'ye range()eşdeğer olmasıdır xrange(). Ve böylece PY3'te xrange()gereksizdir.
Stephen Rauch

-2

comp: ~ $ python Python 2.7.6 (varsayılan, 22 Haz 2015, 17:58:13) linux2'de [GCC 4.8.2]

>>> 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 numarası = 1 param ile:

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

,2245171070098877

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

,10750913619995117

comp: ~ $ python3 Python 3.4.3 (varsayılan, 14 Ekim 2015, 20:28:29) Linux'ta [GCC 4.8.4]

>>> 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 sayısı = 1,2,3,4 param hızlı ve doğrusal bir şekilde çalışır:

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

,09329321900440846

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

,18501482300052885

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

,2703447980020428

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

,36209142999723554

Öyleyse timeit.timeit ("[x, x 100004) aralığında x için x 100004)" sayı = 1) (gerçek kodda kullandığımız gibi) gibi 1 çalışan döngü döngüsünü ölçersek, python3 yeterince hızlı çalışır, ancak tekrarlanan döngülerde python 2 xrange (), python 3'teki aralığa () karşı hız kazanır.


ama bu dilin kendisidir ... xrange / range ile ilgisi yoktur.
mimi.vx
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.