Qual é a diferença entre as funções range e xrange no Python 2.X?

719

Aparentemente, o xrange é mais rápido, mas não tenho idéia do porquê é mais rápido (e não há provas além da anedótica até agora que é mais rápida) ou o que além disso é diferente.

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

Respostas:

817

No Python 2.x:

  • rangecria uma lista; portanto, se você fizer range(1, 10000000)isso, cria uma lista na memória com 9999999elementos.

  • xrange é um objeto de sequência que avalia preguiçosamente.

No Python 3, rangefaz o equivalente ao python xrangee, para obter a lista, você precisa usar list(range(...)).

Charles
fonte
65
O xrange não é exatamente um gerador, mas avalia preguiçosamente e age como um gerador.
Vaibhav Mishra
47
xrange(x).__iter__()é um gerador.
precisa saber é o seguinte
34
Por que eles fizeram xrange, em vez de tornar o intervalo preguiçoso?
Rob Grant
22
@RobertGrant, eles fizeram. Em Python 3. (Eles não poderia fazer isso na linha de 2.x do Python, uma vez que todas as alterações devem ser compatíveis com versões anteriores.)
Paul Draper
12
@Ratul significa que cada um ié avaliado sob demanda e não na inicialização.
Onilol 22/09/2015
223

range cria uma lista; portanto, se você fizer range(1, 10000000)isso, cria uma lista na memória com 9999999elementos.

xrange é um gerador, portanto, é um objeto de sequência que é avaliado preguiçosamente.

Isso é verdade, mas no Python 3, .range()será implementado pelo Python 2 .xrange(). Se você realmente deseja gerar a lista, precisará:

list(range(1,100))
Corey
fonte
3
Não vejo que esse seja um grande problema (em relação à quebra de aplicativos existentes), pois o intervalo era principalmente para gerar índices a serem usados ​​nos loops como "para i no intervalo (1, 10):"
Benjamin Autin,
10
+1 Obrigado por esta resposta, as informações sobre Python 3 substituindo range por xrange são muito úteis. Na verdade, eu disse a alguém de usar xrange vez ou intervalo e eles disseram que ele não importa em python 3, então eu google procurou mais informações e esta resposta veio :)
Cervo
O que há de errado em chamar xrangeum gerador? É uma função que contém yieldinstrução e, de acordo com o glossário, tais funções são chamadas de geradores.
winterlight
@interlight, acho que o termo correto para ele é iterador. Os geradores também devem poder receber.
McSinyx
112

Lembre-se, use o timeitmódulo para testar qual dos pequenos trechos de código é mais rápido!

$ 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

Pessoalmente, eu sempre uso .range(), a menos que eu esteja lidando com listas realmente grandes - como você pode ver, em termos de tempo, para uma lista de um milhão de entradas, a sobrecarga extra é de apenas 0,04 segundos. E, como aponta Corey, no Python 3.0 .xrange()desaparecerá e .range()fornecerá um bom comportamento do iterador de qualquer maneira.

John Fouhy
fonte
12
+1 como exemplo. Nota: para rodar no Windows cmd, é necessário usar aspas duplas, ou seja, ". Portanto, o código serápython -m timeit "for i in xrange(1000000):" " pass"
stalk
10
O principal benefício do xrange é a memória, não o tempo.
endolith
3
+1 para a resposta prática: use o alcance, a menos que seja grande . BTW eles são conceitualmente idênticos, correto? Estranhamente, nenhuma resposta explica isso.
Bob Stein
6
Se o xrange é mais rápido e não monopoliza a memória, por que usar o range?
Austin Mohr
8
Eu concordo com sua afirmação em geral, mas sua avaliação está errada: the extra overhead is only 0.04 secondsnão é a maneira correta de ver isso, (90.5-51.1)/51.1 = 1.771 times sloweré correta porque indica que, se esse é o loop principal do seu programa, ele pode potencialmente estrangulá-lo. No entanto, se essa é uma parte pequena, 1,77x não é muito.
chacham15
65

xrangearmazena apenas os parâmetros do intervalo e gera os números sob demanda. No entanto, a implementação C do Python atualmente restringe seus argumentos a C longs:

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]

Observe que no Python 3.0 existe apenas rangee ele se comporta como o 2.x, xrangemas sem as limitações nos pontos finais mínimos e máximos.

efotinis
fonte
39

O xrange retorna um iterador e mantém apenas um número na memória por vez. O intervalo mantém toda a lista de números na memória.

Ben Hoffstein
fonte
9
xrangese não retornar um iterador.
22715 abarnert
and only keeps one number in memory at a timee onde o resto são colocados por favor me orientar ..
SIslam
5
@SIslam Se souber o início, o fim e a corrente, poderá calcular o próximo, um de cada vez.
Justin Meiners
30

Passe algum tempo com a Referência da biblioteca . Quanto mais familiarizado, mais rápido você encontrará respostas para perguntas como essa. Especialmente importantes são os primeiros capítulos sobre objetos e tipos incorporados.

A vantagem do tipo xrange é que um objeto xrange sempre terá a mesma quantidade de memória, independentemente do tamanho do intervalo que ele representa. Não há vantagens consistentes de desempenho.

Outra maneira de encontrar informações rápidas sobre uma construção Python é a docstring e a função de ajuda:

print xrange.__doc__ # def doc(x): print x.__doc__ is super useful
help(xrange)
Antti Rasinen
fonte
1
A biblioteca é boa, mas nem sempre é fácil obter a resposta para a pergunta que você tem.
Teifion 18/09/08
2
Vá para a referência da biblioteca, pressione ctrl + f, procure o intervalo e você obterá dois resultados. Não é muito esforço encontrar a resposta para esta pergunta.
David Locke
1
A referência da biblioteca não está funcionando. Você pode atualizá-lo?
.. 07/06
14

Estou chocado que ninguém leu doc :

Essa função é muito semelhante a range(), mas retorna um xrangeobjeto em vez de uma lista. Este é um tipo de sequência opaco que gera os mesmos valores da lista correspondente, sem realmente armazená-los todos simultaneamente. A vantagem do xrange()excesso range()é mínima (já que xrange()ainda é necessário criar os valores quando solicitados), exceto quando uma faixa muito grande é usada em uma máquina com falta de memória ou quando todos os elementos da faixa nunca são usados ​​(como quando o loop é geralmente termina com break).

Kishor Pawar
fonte
13

range cria uma lista; portanto, se você fizer o range (1, 10000000), cria uma lista na memória com 10000000 elementos. xrange é um gerador, por isso avalia preguiçosamente.

Isso traz duas vantagens:

  1. Você pode iterar listas mais longas sem obter a MemoryError.
  2. Como ele resolve cada número preguiçosamente, se você interromper a iteração mais cedo, não perderá tempo criando a lista inteira.
Lucas S.
fonte
12

Você encontrará a vantagem de xrangeterminar rangeneste exemplo simples:

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

O exemplo acima não reflete nada substancialmente melhor em caso de xrange.

Agora, observe o seguinte caso em que rangeé realmente muito lento, em comparação com 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

Com range, ele já cria uma lista de 0 a 100000000 (demorada), mas xrangeé um gerador e gera apenas números com base na necessidade, ou seja, se a iteração continuar.

No Python-3, a implementação da rangefuncionalidade é igual à do xrangePython-2, enquanto eles acabaram com o xrangePython-3

Feliz codificação !!

User_Targaryen
fonte
11

É por razões de otimização.

range () criará uma lista de valores do início ao fim (0 .. 20 no seu exemplo). Isso se tornará uma operação cara em faixas muito grandes.

xrange () por outro lado, é muito mais otimizado. ele calculará apenas o próximo valor quando necessário (por meio de um objeto de sequência xrange) e não cria uma lista de todos os valores, como range ().

QAZ
fonte
9

range(x,y)retorna uma lista de cada número entre x e y, se você usar um forloop, rangeé mais lento. De fato, rangepossui uma faixa maior de índice. range(x.y)imprimirá uma lista de todos os números entre x e y

xrange(x,y)retorna, xrange(x,y)mas se você usou um forloop, então xrangeé mais rápido. xrangetem um intervalo menor de índice. xrangenão apenas será impresso, xrange(x,y)mas também manterá todos os números contidos nele.

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

Se você usar um forloop, funcionaria

[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

Não há muita diferença ao usar loops, embora exista uma diferença ao apenas imprimi-lo!

Supercolbat
fonte
8

range (): range (1, 10) retorna uma lista de 1 a 10 números e mantém a lista inteira na memória.

xrange (): como range (), mas em vez de retornar uma lista, retorna um objeto que gera os números no intervalo sob demanda. Para loop, isso é levemente mais rápido que o range () e mais eficiente em memória. xrange () como um iterador e gera os números sob demanda. (Lazy Evaluation)

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
Tushar.PUCSD
fonte
6

Algumas das outras respostas mencionam que o Python 3 eliminou o 2.x rangee renomeou o 2.x xrangepararange . No entanto, a menos que você esteja usando 3.0 ou 3.1 (que ninguém deveria estar), na verdade é um tipo um pouco diferente.

Como dizem os documentos 3.1 :

Os objetos Range têm muito pouco comportamento: eles suportam apenas indexação, iteração e lenfunção.

No entanto, no 3.2+, rangeé uma sequência completa - ele suporta fatias estendidas e todos os métodos collections.abc.Sequencecom a mesma semântica que a list. *

E, pelo menos em CPython e PyPy (os únicos dois 3.2+ implementações que existem atualmente), ele também tem implementações em tempo constante do indexe countmétodos e o inoperador (contanto que você só passá-lo inteiros). Isso significa que a escrita 123456 in ré razoável em 3.2+, enquanto em 2.7 ou 3.1 seria uma ideia horrível.


* O fato de issubclass(xrange, collections.Sequence)retornar Trueem 2.6-2.7 e 3.0-3.1 é um bug corrigido no 3.2 e não suportado.

abarnert
fonte
6

No python 2.x

range (x) retorna uma lista criada na memória com x elementos.

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

xrange (x) retorna um objeto xrange que é um objeto gerador que gera os números sob demanda. eles são calculados durante o loop for (Lazy Evaluation).

Para loop, isso é um pouco mais rápido que o range () e mais eficiente em memória.

>>> b = xrange(5)
>>> b
xrange(5)
Siyaram Malav
fonte
xrange()não é um gerador. xrange(n).__ iter __ () `é.
th3an0maly 11/03/16
5

Ao testar o intervalo contra o xrange em um loop (eu sei que devo usar o timeit , mas isso foi rapidamente hackeado da memória usando um exemplo simples de compreensão de lista), encontrei o seguinte:

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)

que 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?

Ou, usando xrange no loop 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

Meu snippet está testando corretamente? Algum comentário sobre a instância mais lenta do xrange? Ou um exemplo melhor :-)

Dave Everitt
fonte
2
A execução de um benchmark como esse, uma vez, não fornece resultados exatos de tempo. Sempre há uma variação. Pode ser GC ou outro processo que rouba a CPU ... qualquer coisa. É por isso que benchmarks são geralmente executados 10-100-1000 -...
Vajk Hermecz
essa é apenas uma impressão apressada de trechos - eu a executei algumas vezes, mas apenas cerca de 100, e xrangepareceu um pouco mais rápida, embora no Python 3 a comparação agora seja redundante.
91112 Dave
3
É para isso que timeitserve. Ela cuida de correr muitas vezes, incapacitante GC, usando o melhor relógio em vez de time, etc.
abarnert
4

xrange () e range () em python funcionam da mesma forma que para o usuário, mas a diferença ocorre quando estamos falando sobre como a memória é alocada no uso da função.

Quando estamos usando range (), alocamos memória para todas as variáveis ​​que estão gerando, portanto, não é recomendável usar com número maior. de variáveis ​​a serem geradas.

xrange () por outro lado, gera apenas um valor específico de cada vez e só pode ser usado com o loop for para imprimir todos os valores necessários.

Lakshaya Maheshwari
fonte
3

O intervalo gera a lista inteira e a retorna. O xrange não - ele gera os números na lista sob demanda.

Eddie Deyo
fonte
2

O xrange usa um iterador (gera valores rapidamente), range retorna uma lista.

hacama
fonte
2

O que?
rangeretorna uma lista estática em tempo de execução.
xrangeretorna um object(que age como um gerador, embora certamente não seja um) a partir do qual os valores são gerados como e quando necessário.

Quando usar qual?

  • Usar xrange se você deseja gerar uma lista para uma faixa gigantesca, digamos 1 bilhão, especialmente quando você tem um "sistema sensível à memória", como um telefone celular.
  • Use rangese você quiser percorrer a lista várias vezes.

PS: do Python 3.x rangefunção == do Python 2.x xrangefunção.

kmario23
fonte
xrangenão retorna um objeto gerador.
Abarnert
Se bem entendi, é assim que é explicado aqui (para Python 2.x): wiki.python.org/moin/Generators
kmario23
Então o wiki está errado. (Não sei quem é o "SH" quem adicionou e assinou esse comentário.) A documentação oficial está correta; você pode testá-lo e ver se é um gerador ou uma sequência.
Abarnert
Está bem. Mas ele ainda está confuso depois de ler este: stackoverflow.com/questions/135041/...
kmario23
1
A questão diversão é o que fazer quando os discorda intérprete com os documentos oficiais, ou com um intérprete diferente ... Mas, felizmente, que não vem com muita frequência ...
abarnert
2

Todo mundo explicou muito. Mas eu queria que ele visse por mim mesmo. Eu uso python3. Então, abri o monitor de recursos (no Windows!) E, primeiro, executei o seguinte comando primeiro:

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

e depois verificou a alteração na memória 'Em uso'. Foi insignificante. Em seguida, executei o seguinte código:

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

E foi preciso grande parte da memória para uso instantaneamente. E eu estava convencido. Você pode tentar por si mesmo.

Se você estiver usando o Python 2X, substitua 'range ()' por 'xrange ()' no primeiro código e 'list (range ())' por 'range ()'.

ANKUR SATYA
fonte
2

Dos documentos de ajuda.

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

A diferença é aparente. No Python 2.x, rangeretorna uma lista, xrangeretorna um objeto xrange que é iterável.

No Python 3.x, rangepassa a ser xrangedo Python 2.xe xrangeé removido.

Rajendra Uppal
fonte
1

Em um requisito para digitalização / impressão de itens 0-N, o intervalo e o intervalo variam da seguinte maneira.

range () - cria uma nova lista na memória e pega todos os itens de 0 a N (totalmente N + 1) e os imprime. xrange () - cria uma instância do iterador que varre os itens e mantém apenas o item encontrado na memória, utilizando a mesma quantidade de memória o tempo todo.

Caso o elemento requerido esteja apenas no início da lista, ele economiza uma boa quantidade de tempo e memória.

Algumas dúvidas
fonte
1
xrangenão cria uma instância do iterador. Ele cria um xrangeobjeto que é iterável, mas não um iterador - quase (mas não exatamente) uma sequência, como uma lista.
abarnert
1

Range retorna uma lista enquanto xrange retorna um objeto xrange que usa a mesma memória, independentemente do tamanho do intervalo, pois nesse caso, apenas um elemento é gerado e disponível por iteração, enquanto que no caso de usar o intervalo, todos os elementos são gerados de uma só vez e estão disponíveis na memória.

user299567
fonte
1

A diferença diminui para argumentos menores para 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

Nesse caso, xrange(100)é apenas 20% mais eficiente.

Evgeni Sergeev
fonte
1

range: -range irá preencher tudo de uma vez. o que significa que todos os números do intervalo ocuparão a memória.

xrange: -xrange é algo como gerador, ele aparece em imagem quando você deseja o intervalo de números, mas não deseja que eles sejam armazenados, como quando você deseja usar o loop.so, com eficiência de memória.

tejaswini teju
fonte
1

Além disso, se do list(xrange(...))será equivalente a range(...).

Então listé lento.

Também xrangerealmente não termina completamente a sequência

É por isso que não é uma lista, é um xrangeobjeto

Sub-10
fonte
1

range() em Python 2.x

Essa função é essencialmente a range()função antiga que estava disponível no Python 2.xe retorna uma instância de um listobjeto que contém os elementos no intervalo especificado.

No entanto, essa implementação é muito ineficiente quando se trata de inicializar uma lista com um intervalo de números. Por exemplo, for i in range(1000000)seria um comando muito caro para executar, tanto em termos de memória quanto de uso do tempo, pois requer o armazenamento dessa lista na memória.


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

O Python 3.xintroduziu uma implementação mais nova de range()(enquanto a implementação mais recente já estava disponível no Python 2.xatravés doxrange() função).

O range()explora uma estratégia conhecida como avaliação preguiçosa. Em vez de criar uma lista enorme de elementos no intervalo, a implementação mais recente apresenta a classe range, um objeto leve que representa os elementos necessários no intervalo especificado, sem armazená-los explicitamente na memória (isso pode parecer geradores, mas o conceito de avaliação lenta é diferente).


Como exemplo, considere o seguinte:

# 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'>
Giorgos Myrianthous
fonte
-2

Veja este post para encontrar a diferença entre range e xrange:

Citar:

rangeretorna exatamente o que você pensa: uma lista de números inteiros consecutivos, com um comprimento definido começando com 0. xrange, no entanto, retorna um "objeto xrange" , que atua muito como um iterador

Oko
fonte
2
Sei que isso tem 5 anos, mas esse post está errado sobre quase tudo. xrangenão é um iterador. A lista retornada por rangesuporta iteração (uma lista é praticamente o exemplo prototípico de uma iterável). O benefício geral de xrangenão é "mínimo". E assim por diante.
Abarnert