Algumas medições de desempenho, usando em timeit
vez de tentar fazê-lo manualmente time
.
Primeiro, o Apple 2.7.2 de 64 bits:
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
Agora, python.org 3.3.0 de 64 bits:
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
Aparentemente, 3.x range
realmente é um pouco mais lento do que 2.x xrange
. E a xrange
função do OP não tem nada a ver com isso. (Não é surpresa, uma vez que uma chamada única para o __iter__
slot provavelmente não será visível entre 10000000 chamadas para o que acontecer no loop, mas alguém a abordou como uma possibilidade.)
Mas é apenas 30% mais lento. Como o OP ficou 2x mais lento? Bem, se eu repetir os mesmos testes com o Python de 32 bits, recebo 1,58 vs. 3,12. Então, meu palpite é que esse é mais um daqueles casos em que o 3.x foi otimizado para desempenho de 64 bits de maneiras que prejudicam 32 bits.
Mas isso realmente importa? Verifique isso com o 3.3.0 de 64 bits novamente:
In [86]: %timeit [x for x in range(10000000) if x%4 == 0]
1 loops, best of 3: 3.65 s per loop
Portanto, a construção do list
leva mais que o dobro do tempo da iteração inteira.
E quanto a "consome muito mais recursos que o Python 2.6+", nos meus testes, parece que um 3.x range
é exatamente do mesmo tamanho que um 2.x xrange
- e, mesmo que fosse 10 vezes maior, cria a lista desnecessária ainda é 10000000x mais problemático do que qualquer coisa que a iteração de alcance possa fazer.
E o que dizer de um for
loop explícito em vez do loop C dentro 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
Portanto, quase tanto tempo perdido na for
declaração quanto no trabalho real de iterar o range
.
Se você está preocupado em otimizar a iteração de um objeto de intervalo, provavelmente está procurando o lugar errado.
Enquanto isso, você continua perguntando por que xrange
foi removido, não importa quantas vezes as pessoas lhe digam a mesma coisa, mas vou repetir novamente: Não foi removido: foi renomeado para range
e o 2.x range
foi removido.
Aqui estão algumas provas de que o range
objeto 3.3 é um descendente direto do xrange
objeto 2.x (e não da range
função 2.x ): a origem para 3.3range
e 2.7xrange
. Você pode até ver o histórico de alterações (vinculado a, acredito, a alteração que substituiu a última instância da string "xrange" em qualquer lugar do arquivo).
Então, por que é mais lento?
Bem, por um lado, eles adicionaram muitos recursos novos. Por outro lado, eles fizeram todos os tipos de alterações em todo o lugar (especialmente na iteração interna) que têm efeitos colaterais menores. E havia muito trabalho para otimizar dramaticamente vários casos importantes, mesmo que às vezes pessimizasse levemente casos menos importantes. Adicione tudo isso, e não estou surpreso que iterar o range
mais rápido possível seja agora um pouco mais lento. É um daqueles casos menos importantes que ninguém jamais se importaria o suficiente para focar. É provável que ninguém tenha um caso de uso da vida real em que essa diferença de desempenho seja o ponto de acesso em seu código.
range
no Python 3.x éxrange
do Python 2.x. Na verdade, foi o Python 2.xrange
que foi removido.