Xrange () öğesini aralık () üzerinde her zaman tercih etmeli misiniz?


460

Neden ya da neden olmasın?


36
Birisi bizim için python olmayan çocuklar arasındaki farkı kısaca açıklayabilir mi? Belki "xrange () gibi bir şey everyhing range () yapar, ancak X, Y ve Z destekler"
Outlaw Programmer

87
aralık (n), 0..n-1 tamsayılarını içeren bir liste oluşturur. Bu, bir aralık (1000000) yaparsanız bir sorundur, çünkü bir> 4Mb listesiyle sonuçlanırsınız. xrange, bir liste gibi davranan bir nesneyi döndürerek bununla ilgilenir, ancak istenen dizinden gereken sayıyı bulur ve bunu döndürür.
Brian


4
Temel olarak, oysa range(1000)bir olduğu list, xrange(1000)benzer bir hareket eden bir amacı, generator(kesinlikle her ne kadar değil bir). Ayrıca, xrangedaha hızlı. Sen import timeit from timeitve sonra sadece for i in xrange: passve başka bir yöntem yapabilirsiniz range, sonra yapmak timeit(method1)ve timeit(method2)ve, lo ve seyretmek, xrange bazen neredeyse iki kat daha hızlı (yani bir listeye ihtiyacınız yok). (Benim için için i in xrange(1000):passiçin vs i in range(1000):passaldı 13.316725969314575vs 21.190124988555908sırasıyla saniye - çok fazla olduğunu.)
dylnmc

Başka performans testi verir xrange(100)daha hızlı% 20 olarak range(100).
Evgeni Sergeev

Yanıtlar:


443

Performans için, özellikle geniş bir aralıkta yineleme yaparken xrange(), genellikle daha iyidir. Ancak, hala tercih edebileceğiniz birkaç durum var range():

  • Python 3'te, range()eskiden ne xrange()yapar ve xrange()yoktur. Hem Python 2 hem de Python 3'te çalışacak kod yazmak istiyorsanız, kullanamazsınız xrange().

  • range()bazı durumlarda aslında daha hızlı olabilir - ör. aynı dizi üzerinde birden çok kez yineleniyorsa. xrange()tamsayı nesnesini her seferinde yeniden yapılandırmak zorundadır, ancak range()gerçek tamsayı nesnelerine sahip olacaktır. (Ancak bellek açısından her zaman daha kötü performans gösterecektir)

  • xrange()gerçek bir listenin gerekli olduğu her durumda kullanılamaz. Örneğin, dilimleri veya herhangi bir liste yöntemini desteklemez.

[Düzenle] range()2to3 aracı tarafından nasıl yükseltileceğini belirten birkaç yazı var. Kayıt için, aracın bazı örnek kullanımlarında range()vexrange()

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)

Gördüğünüz gibi, bir for döngüsü veya kavramada kullanıldığında veya list () ile önceden sarılmışsa aralık değişmeden kalır.


5
Ne demek "aralık yineleyici olacak"? Bu "jeneratör" olmamalı mı?
Michael Mior

4
Hayır. Jeneratör belirli bir yineleyici türünü ifade eder ve yeni rangeyine de yineleyici değildir.
user2357112 Monica

İkinci merminin gerçekten bir anlamı yok. Bir nesneyi birden çok kez kullanamayacağınızı söylüyorsunuz; tabi ki yapabilirsin! xr = xrange(1,11)sonra bir sonraki satırda deneyin for i in xr: print " ".join(format(i*j,"3d") for j in xr)ve voila! Zaman çizelgeleriniz on taneye kadar. Aynı şekilde çalışır r = range(1,11)ve for i in r: print " ".join(format(i*j,"3d") for j in r)... her şey Python2'de bir nesnedir. Söylemek istediğiniz şey, indeks tabanlı anlama (eğer mantıklıysa) rangeaksine daha iyi yapabileceğinizdir xrange. Menzil çok nadiren kullanışlı, sanırım
dylnmc

Ben, ne kadar (yeterli oda) yapmak olduğunu düşünüyorum rangeEğer kullanmak istiyorsanız kullanışlı olabilir listbir döngü içinde ve daha sonra bu listeye belirli koşullar veya ekleme şeylere dayanan bazı dizinlerinin değiştirebilir ve ardından rangekesinlikle daha iyidir. Ancak, xrangedaha hızlıdır ve daha az bellek kullanır, bu nedenle döngü uygulamalarının çoğunluğu için en iyisi gibi görünmektedir. Sorunun sorusuna geri dönme - nadiren ama var olan, rangedaha iyi olacağı durumlar vardır. Belki de düşündüğüm kadar nadir değil, ama kesinlikle xrange% 95'ini kullanıyorum .
dylnmc

129

Hayır, her ikisinin de kullanımları var:

xrange()Bellek tasarrufu sağladığı için yineleme yaparken kullanın . Söyle:

for x in xrange(1, one_zillion):

ziyade:

for x in range(1, one_zillion):

Öte yandan, range()gerçekten bir sayı listesi istiyorsanız kullanın .

multiples_of_seven = range(7,100,7)
print "Multiples of seven < 100: ", multiples_of_seven

42

Sen iyilik etmelidir range()üzerinde xrange()Eğer gerçek bir liste yalnızca gereksinim duyduğunuzda. Örneğin, döndürülen listeyi değiştirmek range()istediğinizde veya dilimlemek istediğinizde. Yineleme veya hatta normal indeksleme için, xrange()iyi çalışır (ve genellikle çok daha verimli). Çok küçük listelere range()göre biraz daha hızlı olan bir nokta vardır xrange(), ancak donanımınıza ve diğer çeşitli ayrıntılara bağlı olarak başabaş, 1 veya 2 uzunluğunun bir sonucu olabilir; endişelenecek bir şey değil. Tercih et xrange().


30

Diğer bir fark, xrange () öğesinin C inç'ten daha büyük sayıları destekleyememesidir, bu nedenle python'un yerleşik büyük sayı desteğini kullanarak bir aralığa sahip olmak istiyorsanız range () öğesini kullanmanız gerekir.

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'te bu sorun yoktur:

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()daha verimlidir çünkü bir nesne listesi oluşturmak yerine, her seferinde sadece bir nesne üretir. 100 tamsayı ve bunların tüm ek yükleri ve bunları koyacağınız liste yerine, tek seferde yalnızca bir tamsayı vardır. Daha hızlı üretim, daha iyi bellek kullanımı, daha verimli kod.

Özellikle bir şey için bir listeye ihtiyacım yoksa, her zaman xrange()


8

range () bir liste döndürür, xrange () bir xrange nesnesi döndürür.

xrange () biraz daha hızlı ve bellekte daha verimli. Ancak kazanç çok büyük değil.

Bir liste tarafından kullanılan ekstra bellek elbette sadece israf değildir, listelerin daha fazla işlevselliği vardır (dilim, tekrar, ekle, ...). Kesin farklılıklar dokümanlarda bulunabilir . Kemik sert kuralı yoktur, gerekli olanı kullanın.

Python 3.0 hala geliştirilmektedir, ancak IIRC aralığı () 2.X'in xrange () işlevine çok benzer ve liste oluşturmak için liste (aralık ()) kullanılabilir.


5

Ben sadece dilim ve indeksleme işlevselliği ile xrange bir nesne elde etmek gerçekten zor olmadığını söylemek istiyorum. Ben oldukça dang iyi çalışır ve ne zaman (yinelemeler) için xrange kadar hızlı bazı kod yazdım.

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)

Dürüst olmak gerekirse, bence tüm mesele aptalca ve xrange tüm bunları yine de yapmalı ...


Evet kabul etti; tamamen farklı bir teknolojiden, tembel hale getirmek için lodash üzerindeki çalışmayı kontrol edin: github.com/lodash/lodash/issues/274 . Dilimleme vb. Mümkün olduğunca tembel olmalı ve olmasa da, ancak o zaman yeniden birleştirin.
Rob Grant

4

Kitapta iyi bir örnek: Pratik Python By Magnus Lie Hetland

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

Önceki örnekte xrange yerine aralık kullanılmasını önermem - yalnızca ilk beş sayı gerekli olsa da, aralık tüm sayıları hesaplar ve bu çok zaman alabilir. Xrange ile bu bir sorun oluşturmaz, çünkü sadece gereken sayıları hesaplar.

Evet @ Brian'ın cevabını okudum: Python 3'te range () zaten bir jeneratör ve xrange () mevcut değil.


3

Bu nedenlerle menzil ile devam edin:

1) xrange yeni Python sürümlerinde kullanımdan kaldırılacak. Bu size gelecekteki kolay uyumluluğu sağlar.

2) menzil xrange ile ilgili verimlilikleri üstlenecektir.


13
Bunu yapma. xrange () ortadan kalkar, ama bir sürü başka şey de gider. Python 2.x kodunuzu Python 3.x koduna çevirmek için kullanacağınız araç otomatik olarak xrange () öğesini range () öğesine çevirecektir, ancak range () daha az verimli listeye (range ()) çevrilecektir.
Thomas Wouters

10
Thomas Douglas: Aslında bundan biraz daha akıllı. Gerçek bir listeye (ör. Bir döngü veya kavramada) ihtiyaç duymadığı durumlarda range () öğesini yalnızca düz aralığa () çevirecektir. Yalnızca bir değişkene atandığı veya doğrudan kullanıldığı durumlarda liste () ile sarılmalıdır
Brian

2

Tamam, buradaki herkes, xrange'in menzil karşısındaki avantajları ve avantajları hakkında farklı bir görüş olarak. Çoğunlukla doğrudurlar, xrange bir yineleyicidir ve menzil etler ve gerçek bir liste oluşturur. Çoğu durumda, ikisi arasında gerçekten bir fark görmeyeceksiniz. (Haritayı menzilli kullanabilirsiniz ancak xrange ile kullanamazsınız, ancak daha fazla bellek kullanır.)

Ralli duymak istediğini düşündüğüm şey, tercih edilen seçeneğin xrange olmasıdır. Python 3'teki aralık bir yineleyici olduğundan, kod dönüştürme aracı 2to3, xrange'in tüm kullanımlarını aralığa doğru dönüştürecek ve aralık kullanımları için bir hata veya uyarı verecektir. İleride kodunuzu kolayca dönüştüreceğinizden emin olmak istiyorsanız, yalnızca xrange'i ve bir liste istediğinizden emin olduğunuzda listeyi (xrange) kullanırsınız. Bunu bu yıl Chicago'da PyCon'daki CPython sprint sırasında öğrendim.


8
Bu doğru değil. "Aralık (20) içindeki x için" gibi kod aralık olarak bırakılır ve "x = aralık (20)" gibi kod "x = list (aralık (20))" olarak dönüştürülür - hata yok. Ayrıca, hem 2.6 hem de 3.0 altında çalışacak kod yazmak istiyorsanız, range (), uyumluluk işlevleri eklemeden tek seçeneğinizdir.
Brian

2
  • range(): range(1, 10)1'den 10'a kadar sayılardan oluşan bir liste döndürür ve listenin tamamını bellekte tutar.
  • xrange(): Gibi range(), ancak bir liste döndürmek yerine, isteğe bağlı aralıktaki sayıları üreten bir nesne döndürür. Döngü için bu, range()bellekten biraz daha hızlı ve daha verimlidir. xrange()bir yineleyici gibi nesne ve talep üzerine sayılar üretir (Tembel Değerlendirme).
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'te kullanılanla aynı şeyi yapar ve Python 3'te terim xrange()yoktur. range()Aynı senaryoyu birden çok kez tekrarlarsanız bazı senaryolarda aslında daha hızlı olabilir. xrange()tamsayı nesnesini her seferinde yeniden yapılandırmak zorundadır, ancak range()gerçek tamsayı nesnelerine sahip olacaktır.


2

Çoğu durumda xrangeolduğundan daha hızlı olsa da range, performans farkı oldukça azdır. Aşağıdaki küçük program a rangeve an arasındaki yinelemeleri karşılaştırır 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)

Aşağıdaki sonuçlar xrangebunun gerçekten daha hızlı olduğunu, ancak terlemek için yeterli olmadığını göstermektedir.

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

Elbette kullanın xrange, ancak kısıtlı bir donanımda değilseniz, bu konuda fazla endişelenmeyin.


Sizin list_lenkullanılan ve sadece uzunluk 100 listelerine yönelik bu kodu koşuyoruz böylece varlık değildir
Mark

Aslında liste uzunluğunu değiştirmenizi tavsiye ederim:rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000)
Mark

Vay canına, bu iyi bir hafta olacak, teşekkürler, düzeltildi. Yine de, büyük bir fark değil.
Speedplane
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.