Qual è la differenza tra le funzioni range e xrange in Python 2.X?


719

Apparentemente xrange è più veloce, ma non ho idea del perché sia ​​più veloce (e nessuna prova oltre all'aneddoto finora che è più veloce) o cosa oltre a ciò è diverso

for i in range(0, 20):
for i in xrange(0, 20):

Risposte:


817

In Python 2.x:

  • rangecrea un elenco, quindi se lo fai range(1, 10000000)crea un elenco in memoria con 9999999elementi.

  • xrange è un oggetto sequenza che valuta pigramente.

In Python 3, rangefa l'equivalente di Python xrangee, per ottenere l'elenco, devi usare list(range(...)).


65
xrange non è esattamente un generatore ma valuta pigramente e si comporta come un generatore.
Vaibhav Mishra,

47
xrange(x).__iter__()è un generatore.
augustomen

34
Perché hanno fatto xrange, piuttosto che rendere il range pigro?
Rob Grant,

22
@RobertGrant, l'hanno fatto. In Python 3. (Non potevano farlo nella linea Python 2.x, poiché tutte le modifiche devono essere retrocompatibili.)
Paul Draper

12
@Ratul significa che ognuno iviene valutato su richiesta anziché su inizializzazione.
Onilol,

223

range crea un elenco, quindi se lo fai range(1, 10000000)crea un elenco in memoria con 9999999elementi.

xrange è un generatore, quindi è un oggetto sequenza è una che valuta pigramente.

Questo è vero, ma in Python 3 .range()sarà implementato da Python 2 .xrange(). Se è necessario generare effettivamente l'elenco, è necessario:

list(range(1,100))

3
Non vedo che si tratta di un grosso problema (per quanto riguarda la rottura delle applicazioni esistenti) poiché l'intervallo era principalmente per la generazione di indici da utilizzare per i loop come "per i nell'intervallo (1, 10):"
Benjamin Autin,

10
+1 Grazie per questa risposta, le informazioni su Python 3 che sostituiscono l'intervallo con xrange sono molto utili. In realtà ho detto a qualcuno di usare xrange invece o range e hanno detto che non aveva importanza in Python 3, quindi ho cercato su Google ulteriori informazioni e questa risposta è arrivata :)
Cervo,

Cosa c'è di sbagliato nel chiamare xrangeun generatore? È una funzione che contiene yieldun'istruzione e secondo il glossario tali funzioni sono chiamate generatori.
Winterlight,

@winterlight, pensa che il termine corretto sia iteratore. Anche i generatori dovrebbero essere in grado di ricevere.
McSinyx

112

Ricorda, usa il timeitmodulo per testare quale dei piccoli frammenti di codice è più veloce!

$ python -m timeit 'for i in range(1000000):' ' pass'
10 loops, best of 3: 90.5 msec per loop
$ python -m timeit 'for i in xrange(1000000):' ' pass'
10 loops, best of 3: 51.1 msec per loop

Personalmente, uso sempre .range(), a meno che non abbia a che fare con elenchi davvero enormi - come puoi vedere, in termini di tempo, per un elenco di un milione di voci, l'overhead aggiuntivo è di soli 0,04 secondi. E come sottolinea Corey, in Python 3.0 .xrange()sparirà e .range()ti darà comunque un buon comportamento da iteratore.


12
+1 per esempio timeit. Nota: per eseguire in Windows cmd è necessario utilizzare la doppia virgoletta, ad esempio ". Quindi il codice saràpython -m timeit "for i in xrange(1000000):" " pass"
gambo

10
Il vantaggio principale di xrange è la memoria, non il tempo.
endolith

3
+1 per la risposta pratica: usa il range a meno che non sia enorme . A proposito sono concettualmente identici, corretti? Stranamente nessuna risposta lo spiega.
Bob Stein,

6
Se xrange è più veloce e non sopporta la memoria, perché mai usare range?
Austin Mohr,

8
Sono d'accordo con la tua affermazione in generale, ma la tua valutazione è sbagliata: the extra overhead is only 0.04 secondsnon è il modo corretto di esaminarla, (90.5-51.1)/51.1 = 1.771 times slowerè corretta perché trasmette che se questo è il ciclo principale del tuo programma, può potenzialmente colmare il collo di bottiglia. Tuttavia, se questa è una piccola parte, 1.77x non è molto.
Chacham15,

65

xrangememorizza solo i parametri di intervallo e genera i numeri su richiesta. Tuttavia l'implementazione C di Python attualmente limita i suoi argomenti a C long:

xrange(2**32-1, 2**32+1)  # When long is 32 bits, OverflowError: Python int too large to convert to C long
range(2**32-1, 2**32+1)   # OK --> [4294967295L, 4294967296L]

Si noti che in Python 3.0 c'è solo rangee si comporta come il 2.x xrangema senza le limitazioni sui punti minimo e massimo.


39

xrange restituisce un iteratore e mantiene un solo numero in memoria alla volta. range mantiene l'intero elenco di numeri in memoria.


9
xrangenon non restituisce un iteratore.
abarnert,

and only keeps one number in memory at a timee dove sono collocati gli altri, per favore
guidami

5
@SIslam Se conosce l'inizio, la fine e l'attuale, può calcolare il successivo, uno alla volta.
Justin Meiners,

30

Trascorri del tempo con il riferimento alla libreria . Più familiarità con esso, più veloce è possibile trovare risposte a domande come questa. Particolarmente importanti sono i primi capitoli su oggetti e tipi incorporati.

Il vantaggio del tipo xrange è che un oggetto xrange occuperà sempre la stessa quantità di memoria, indipendentemente dalla dimensione dell'intervallo che rappresenta. Non ci sono vantaggi prestazionali costanti.

Un altro modo per trovare rapidamente informazioni su un costrutto Python è il docstring e la funzione di aiuto:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)

1
La biblioteca è buona ma non è sempre così facile ottenere la risposta alla domanda che hai.
Teifion,

2
Vai al riferimento della libreria, premi ctrl + f, cerca l'intervallo e otterrai due risultati. Non è molto difficile trovare la risposta a questa domanda.
David Locke,

1
Il riferimento alla libreria non funziona. Puoi aggiornarlo per favore?
mk ..

14

Sono scioccato che nessuno legga doc :

Questa funzione è molto simile a range(), ma restituisce un xrangeoggetto anziché un elenco. Questo è un tipo di sequenza opaca che produce gli stessi valori dell'elenco corrispondente, senza effettivamente memorizzarli tutti contemporaneamente. Il vantaggio di xrange()over range()è minimo (poiché xrange()deve ancora creare i valori quando richiesto) tranne quando viene utilizzato un intervallo molto ampio su una macchina affamata di memoria o quando non vengono mai utilizzati tutti gli elementi dell'intervallo (come quando il ciclo è di solito terminato con break).


13

range crea un elenco, quindi se lo fai range (1, 10000000) crea un elenco in memoria con 10000000 elementi. xrange è un generatore, quindi valuta pigramente.

Ciò offre due vantaggi:

  1. Puoi iterare elenchi più lunghi senza ottenere a MemoryError.
  2. Poiché risolve pigramente ogni numero, se interrompi l'iterazione in anticipo, non perderai tempo a creare l'intero elenco.

12

Troverai il vantaggio di xrangeover rangein questo semplice esempio:

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 4.49153590202 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    pass
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 7.04547905922 seconds

L'esempio sopra non riflette nulla di sostanzialmente migliore in caso di xrange.

Ora guarda il seguente caso in cui rangeè davvero molto lento, rispetto a xrange.

import timeit

t1 = timeit.default_timer()
a = 0
for i in xrange(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer()

print "time taken: ", (t2-t1)  # 0.000764846801758 seconds

t1 = timeit.default_timer()
a = 0
for i in range(1, 100000000):
    if i == 10000:
        break
t2 = timeit.default_timer() 

print "time taken: ", (t2-t1)  # 2.78506207466 seconds

Con range, crea già un elenco da 0 a 100000000 (che richiede tempo), ma xrangeè un generatore e genera solo numeri in base alla necessità, vale a dire se l'iterazione continua.

In Python-3, l'implementazione della rangefunzionalità è la stessa di xrangePython-2, mentre xrangein Python-3 è stata eliminata

Buona programmazione !!


11

È per motivi di ottimizzazione.

range () creerà un elenco di valori dall'inizio alla fine (0 .. 20 nel tuo esempio). Questo diventerà un'operazione costosa su gamme molto grandi.

xrange () d'altra parte è molto più ottimizzato. calcolerà il valore successivo solo quando necessario (tramite un oggetto sequenza xrange) e non crea un elenco di tutti i valori come range ().


9

range(x,y)restituisce un elenco di ciascun numero tra xey se si utilizza un forciclo, quindi rangeè più lento. In effetti, rangeha una gamma di indici più ampia. range(x.y)stamperà un elenco di tutti i numeri tra x e y

xrange(x,y)ritorna xrange(x,y)ma se hai usato un forloop, allora xrangeè più veloce. xrangeha un intervallo di indice più piccolo. xrangenon solo stamperà xrange(x,y)ma manterrà comunque tutti i numeri che ci sono dentro.

[In] range(1,10)
[Out] [1, 2, 3, 4, 5, 6, 7, 8, 9]
[In] xrange(1,10)
[Out] xrange(1,10)

Se usi un forloop, allora funzionerebbe

[In] for i in range(1,10):
        print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9
[In] for i in xrange(1,10):
         print i
[Out] 1
      2
      3
      4
      5
      6
      7
      8
      9

Non c'è molta differenza quando si usano i loop, anche se c'è una differenza quando si stampa!


8

range (): range (1, 10) restituisce un elenco da 1 a 10 numeri e mantiene l'intero elenco in memoria.

xrange (): come range (), ma invece di restituire un elenco, restituisce un oggetto che genera i numeri nell'intervallo su richiesta. Per il looping, questo è leggermente più veloce di range () e più efficiente della memoria. xrange () oggetto come un iteratore e genera i numeri su richiesta. (Valutazione pigra)

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__

xrange([start,] stop[, step]) -> xrange object

6

Alcune delle altre risposte menzionano che Python 3 ha eliminato 2.x rangee rinominato 2.x xrangein range. Tuttavia, a meno che non si utilizzi 3.0 o 3.1 (che nessuno dovrebbe essere), in realtà è un tipo leggermente diverso.

Come dicono i documenti 3.1 :

Gli oggetti intervallo hanno un comportamento molto limitato: supportano solo indicizzazione, iterazione e lenfunzione.

Tuttavia, in 3.2+, rangeè una sequenza completa: supporta sezioni estese e tutti i metodi collections.abc.Sequencecon la stessa semantica di a list. *

E, almeno in CPython e PyPy (gli unici due 3.2+ implementazioni attualmente esistenti), ma ha anche implementazioni costante di tempo del indexe countmetodi e l' inoperatore (a patto che si passa solo numeri interi). Ciò significa che la scrittura 123456 in rè ragionevole in 3.2+, mentre in 2.7 o 3.1 sarebbe un'idea orribile.


* Il fatto che issubclass(xrange, collections.Sequence)ritorni Truein 2.6-2.7 e 3.0-3.1 è un bug che è stato corretto in 3.2 e non eseguito il backport.


6

In Python 2.x

range (x) restituisce un elenco, che viene creato in memoria con x elementi.

>>> a = range(5)
>>> a
[0, 1, 2, 3, 4]

xrange (x) restituisce un oggetto xrange che è un oggetto generatore che genera i numeri su richiesta. vengono calcolati durante il for-loop (Lazy Evaluation).

Per il loop, questo è leggermente più veloce di range () e più efficiente della memoria.

>>> b = xrange(5)
>>> b
xrange(5)

xrange()non è un generatore. xrange(n).__ iter __ () `è.
th3an0maly,

5

Quando ho testato range contro xrange in un ciclo (so che dovrei usare timeit , ma questo è stato rapidamente cancellato dalla memoria usando un semplice esempio di comprensione dell'elenco) ho trovato quanto segue:

import time

for x in range(1, 10):

    t = time.time()
    [v*10 for v in range(1, 10000)]
    print "range:  %.4f" % ((time.time()-t)*100)

    t = time.time()
    [v*10 for v in xrange(1, 10000)]
    print "xrange: %.4f" % ((time.time()-t)*100)

che dà:

$python range_tests.py
range:  0.4273
xrange: 0.3733
range:  0.3881
xrange: 0.3507
range:  0.3712
xrange: 0.3565
range:  0.4031
xrange: 0.3558
range:  0.3714
xrange: 0.3520
range:  0.3834
xrange: 0.3546
range:  0.3717
xrange: 0.3511
range:  0.3745
xrange: 0.3523
range:  0.3858
xrange: 0.3997 <- garbage collection?

Oppure, usando xrange nel ciclo for:

range:  0.4172
xrange: 0.3701
range:  0.3840
xrange: 0.3547
range:  0.3830
xrange: 0.3862 <- garbage collection?
range:  0.4019
xrange: 0.3532
range:  0.3738
xrange: 0.3726
range:  0.3762
xrange: 0.3533
range:  0.3710
xrange: 0.3509
range:  0.3738
xrange: 0.3512
range:  0.3703
xrange: 0.3509

Il mio frammento sta eseguendo correttamente il test? Qualche commento sull'istanza più lenta di xrange? O un esempio migliore :-)


2
L'esecuzione di un benchmark come questo, una volta, non fornisce risultati di temporizzazione esatti. C'è sempre una varianza .. Potrebbe essere GC o un altro processo che ruba la CPU ... qualsiasi cosa. Ecco perché i benchmark vengono generalmente eseguiti 10-100-1000 -...
Vajk Hermecz,

questa è solo una stampa frettolosa dello snippet: l'ho eseguita alcune volte, ma solo fino a circa 100, e xrangesembrava leggermente più veloce, anche se con Python 3 il confronto è ora ridondante.
Dave Everitt,

3
Questo è ciò che timeitserve. Si occupa di eseguire molte volte, disabilitare GC, utilizzare l'orologio migliore anziché time, ecc.
abarnert,

4

xrange () e range () in python funzionano in modo simile a quello dell'utente, ma la differenza viene quando parliamo di come viene allocata la memoria usando entrambe le funzioni.

Quando stiamo usando range () allociamo la memoria per tutte le variabili che sta generando, quindi non è consigliabile usare con no più grandi. delle variabili da generare.

xrange () d'altra parte genera solo un valore particolare alla volta e può essere utilizzato solo con il ciclo for per stampare tutti i valori richiesti.


3

range genera l'intero elenco e lo restituisce. xrange no: genera i numeri nell'elenco su richiesta.


2

xrange utilizza un iteratore (genera valori al volo), range restituisce un elenco.


2

Che cosa?
rangerestituisce un elenco statico in fase di esecuzione.
xrangerestituisce un object(che si comporta come un generatore, sebbene non sia certamente uno) da cui vengono generati i valori come e quando richiesto.

Quando usare quale?

  • Uso xrange se si desidera generare un elenco per un intervallo gigantesco, ad esempio 1 miliardo, soprattutto quando si dispone di un "sistema sensibile alla memoria" come un telefono cellulare.
  • Utilizzare rangese si desidera scorrere più volte l'elenco.

PS: di Python 3.x rangefunzione == di Python 2.x xrangefunzione.


xrangenon restituisce un oggetto generatore.
abarnert,

Se ho capito bene, è così che viene spiegato qui (per Python 2.x): wiki.python.org/moin/Generators
kmario23

Quindi il wiki è sbagliato. (Non so chi sia lo "SH" che ha aggiunto e firmato quel commento.) La documentazione ufficiale è corretta; puoi testarlo tu stesso e vedere se è un generatore o una sequenza.
abarnert,

ok. Ma è ancora confuso dopo aver letto questo: stackoverflow.com/questions/135041/...
kmario23

1
La domanda divertente è cosa fare quando l'interprete non è d'accordo con i documenti ufficiali o con un altro interprete ... Ma per fortuna, ciò non si presenta troppo spesso ...
abarnert,

2

Tutti lo hanno spiegato molto. Ma volevo vederlo da solo. Uso python3. Quindi, ho aperto il monitor risorse (in Windows!) E prima ho eseguito prima il comando seguente:

a=0
for i in range(1,100000):
    a=a+i

e quindi ho verificato la modifica nella memoria "In uso". Era insignificante. Quindi, ho eseguito il seguente codice:

for i in list(range(1,100000)):
    a=a+i

E ci volle un grosso pezzo di memoria per usarla, all'istante. E ne ero convinto. Puoi provarlo tu stesso.

Se stai usando Python 2X, sostituisci 'range ()' con 'xrange ()' nel primo codice e 'list (range ())' con 'range ()'.


2

Dai documenti di aiuto.

Python 2.7.12

>>> print range.__doc__
range(stop) -> list of integers
range(start, stop[, step]) -> list of integers

Return a list containing an arithmetic progression of integers.
range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
When step is given, it specifies the increment (or decrement).
For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
These are exactly the valid indices for a list of 4 elements.

>>> print xrange.__doc__
xrange(stop) -> xrange object
xrange(start, stop[, step]) -> xrange object

Like range(), but instead of returning a list, returns an object that
generates the numbers in the range on demand.  For looping, this is 
slightly faster than range() and more memory efficient.

Python 3.5.2

>>> print(range.__doc__)
range(stop) -> range object
range(start, stop[, step]) -> range object

Return an object that produces a sequence of integers from start (inclusive)
to stop (exclusive) by step.  range(i, j) produces i, i+1, i+2, ..., j-1.
start defaults to 0, and stop is omitted!  range(4) produces 0, 1, 2, 3.
These are exactly the valid indices for a list of 4 elements.
When step is given, it specifies the increment (or decrement).

>>> print(xrange.__doc__)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'xrange' is not defined

La differenza è evidente. In Python 2.x, rangerestituisce un elenco, xrangerestituisce un oggetto xrange che è iterabile.

In Python 3.x rangediventa xrangePython 2.x e xrangeviene rimosso.


1

Su un requisito per la scansione / stampa di articoli 0-N, range e xrange funzionano come segue.

range () - crea un nuovo elenco in memoria e prende tutti gli elementi da 0 a N (totalmente N + 1) e li stampa. xrange () - crea un'istanza iteratore che esegue la scansione degli elementi e mantiene in memoria solo l'elemento corrente rilevato, quindi utilizzando sempre la stessa quantità di memoria.

Nel caso in cui l'elemento richiesto sia in qualche modo all'inizio dell'elenco, si risparmia una buona quantità di tempo e memoria.


1
xrangenon crea un'istanza iteratore. Crea un xrangeoggetto, che è iterabile, ma non un iteratore, quasi (ma non del tutto) una sequenza, come un elenco.
abarnert,

1

Range restituisce un elenco mentre xrange restituisce un oggetto xrange che occupa la stessa memoria indipendentemente dalle dimensioni dell'intervallo, poiché in questo caso viene generato e disponibile un solo elemento per iterazione mentre in caso di utilizzo dell'intervallo, tutti gli elementi vengono generati contemporaneamente e sono disponibili in memoria.


1

La differenza diminuisce per argomenti più piccoli su range(..)/ xrange(..):

$ python -m timeit "for i in xrange(10111):" " for k in range(100):" "  pass"
10 loops, best of 3: 59.4 msec per loop

$ python -m timeit "for i in xrange(10111):" " for k in xrange(100):" "  pass"
10 loops, best of 3: 46.9 msec per loop

In questo caso xrange(100)è solo circa il 20% più efficiente.


1

range: -range popolerà tutto in una volta, il che significa che ogni numero dell'intervallo occuperà la memoria.

xrange: -xrange è qualcosa di simile al generatore, entrerà in scena quando si desidera l'intervallo di numeri ma non si desidera che vengano memorizzati, come quando si desidera utilizzarlo per la memoria di loop.so efficiente.


1

Inoltre, if do list(xrange(...))sarà equivalente a range(...).

Quindi listè lento.

Inoltre, in xrangerealtà non completa completamente la sequenza

Ecco perché non è un elenco, è un xrangeoggetto


1

range() in Python 2.x

Questa funzione è essenzialmente la vecchia range()funzione disponibile in Python 2.xe restituisce un'istanza di un listoggetto che contiene gli elementi nell'intervallo specificato.

Tuttavia, questa implementazione è troppo inefficiente quando si tratta di inizializzare un elenco con un intervallo di numeri. Ad esempio, for i in range(1000000)sarebbe un comando molto costoso da eseguire, sia in termini di memoria che di utilizzo del tempo in quanto richiede la memorizzazione di questo elenco nella memoria.


range()in Python 3.xe xrange()in Python2.x

Python ha 3.xintrodotto un'implementazione più recente di range()(mentre l'implementazione più recente era già disponibile in Python 2.xtramitexrange() funzione).

Il range()sfrutta una strategia nota come valutazione pigra. Invece di creare un vasto elenco di elementi nel raggio d'azione, l'implementazione più recente introduce la classe range, un oggetto leggero che rappresenta gli elementi richiesti nell'intervallo dato, senza memorizzarli esplicitamente in memoria (questo potrebbe sembrare generatori ma il concetto di valutazione lenta è diverso).


Ad esempio, considerare quanto segue:

# Python 2.x
>>> a = range(10)
>>> type(a)
<type 'list'>
>>> b = xrange(10)
>>> type(b)
<type 'xrange'>

e

# Python 3.x
>>> a = range(10)
>>> type(a)
<class 'range'>

-2

Vedi questo post per trovare la differenza tra range e xrange:

Per citare:

rangerestituisce esattamente quello che pensi: un elenco di numeri interi consecutivi, di una lunghezza definita che inizia con 0. xrange, tuttavia, restituisce un "oggetto xrange" , che agisce molto come un iteratore


2
Mi rendo conto che ha 5 anni, ma quel post è sbagliato su quasi tutto. xrangenon è un iteratore. L'elenco restituito da rangesupporta l'iterazione (un elenco è praticamente l'esempio prototipico di un iterabile). Il vantaggio complessivo di xrangenon è "minimo". E così via.
abarnert,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.