Jaka jest różnica między funkcjami zasięgu i zakresu x w Pythonie 2.X?


718

Najwyraźniej xrange jest szybszy, ale nie mam pojęcia, dlaczego jest szybszy (i nie ma żadnego dowodu poza anegdotą do tej pory, że jest szybszy) lub co poza tym jest inne

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

Odpowiedzi:


817

W Python 2.x:

  • rangetworzy listę, więc jeśli to zrobisz range(1, 10000000), tworzy listę w pamięci z 9999999elementami.

  • xrange jest obiektem sekwencji, który ocenia leniwie.

W Pythonie 3 rangerobi odpowiednik Pythona xrangei aby uzyskać listę, musisz użyć list(range(...)).


65
xrange nie jest dokładnie generatorem, ale ocenia leniwie i działa jak generator.
Vaibhav Mishra

47
xrange(x).__iter__()jest generatorem.
augustomen

34
Dlaczego zmienili Xrange zamiast leniwie w zasięg?
Rob Grant

22
@RobertGrant, zrobili to. W Python 3. (Nie mogli tego zrobić w linii Python 2.x, ponieważ wszystkie zmiany muszą być kompatybilne wstecz).
Paul Draper,

12
@Ratul oznacza, że ​​każdy ijest oceniany na żądanie, a nie podczas inicjalizacji.
Onilol

223

zakres tworzy listę, więc jeśli to zrobisz range(1, 10000000), tworzy listę w pamięci z 9999999elementami.

xrange Jest to generator, więc jest obiektem sekwencji to , że ocenia się leniwie.

To prawda, ale w Pythonie 3 .range()zostanie zaimplementowane przez Python 2 .xrange(). Jeśli musisz wygenerować listę, musisz:

list(range(1,100))

3
Nie widzę, aby był to duży problem (dotyczący zerwania z istniejącymi aplikacjami), ponieważ zakres służył głównie do generowania indeksów używanych w pętlach jako „dla i w zakresie (1, 10):”
Benjamin Autin

10
+1 Dzięki za tę odpowiedź informacja o zamianie zakresu w Pythonie 3 na xrange jest bardzo przydatna. Właściwie powiedziałem komuś, by użył xrange zamiast lub zakresu, a oni powiedzieli, że to nie ma znaczenia w Pythonie 3, więc google szukałem więcej informacji i ta odpowiedź pojawiła się :)
Cervo

Co jest złego w wywoływaniu xrangegeneratora? Jest to funkcja zawierająca yieldinstrukcję i zgodnie ze słownikiem funkcje takie nazywane są generatorami.
zimowe światło

@winterlight, pomyśl, że poprawnym terminem jest iterator. Generatory również powinny móc odbierać.
McSinyx

112

Pamiętaj, użyj timeitmodułu do przetestowania, który z małych fragmentów kodu jest szybszy!

$ 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

Osobiście zawsze używam .range(), chyba że mam do czynienia z naprawdę dużymi listami - jak widać pod względem czasowym, dla listy milionów wpisów, dodatkowy narzut wynosi tylko 0,04 sekundy. I, jak zauważa Corey, w Pythonie 3.0 .xrange()odejdzie i .range()da ci ładne zachowanie iteratora.


12
+1 za przykład czasu. Uwaga: aby uruchomić w Windows cmd, należy użyć podwójnego cudzysłowu, tj. ". Więc kod będziepython -m timeit "for i in xrange(1000000):" " pass"
prześledzić

10
Główną zaletą Xrange jest pamięć, a nie czas.
endolith

3
+1 za praktyczną odpowiedź: użyj zasięgu, chyba że ogromny . BTW, są koncepcyjnie identyczne, prawda? Co dziwne, żadna odpowiedź tego nie wyjaśnia.
Bob Stein

6
Jeśli xrange jest szybszy i nie zajmuje się pamięcią, to po co kiedykolwiek używać zakresu?
Austin Mohr

8
Zgadzam się z twoim oświadczeniem, ale twoja ocena jest błędna: the extra overhead is only 0.04 seconds nie jest to właściwy sposób patrzenia na to, (90.5-51.1)/51.1 = 1.771 times slowerjest poprawna, ponieważ przekazuje, że jeśli jest to główna pętla twojego programu, może to potencjalnie wąskie gardło. Jeśli jednak jest to niewielka część, 1,77x to niewiele.
chacham15,

65

xrangeprzechowuje tylko parametry zakresu i generuje liczby na żądanie. Jednak implementacja języka C w Pythonie obecnie ogranicza swoje argumenty do długości C:

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]

Zauważ, że w Pythonie 3.0 jest tylko rangei zachowuje się jak 2.x, xrangeale bez ograniczeń minimalnych i maksymalnych punktów końcowych.


39

xrange zwraca iterator i zachowuje tylko jedną liczbę w pamięci na raz. zakres zachowuje całą listę liczb w pamięci.


9
xrangenie nie zwraca iterator.
abarnert

and only keeps one number in memory at a timea gdzie reszta jest umieszczona, proszę poprowadź mnie ..
SIslam

5
@SIslam Jeśli zna początek, koniec i prąd, może obliczyć następny, jeden po drugim.
Justin Meiners,

30

Spędź trochę czasu z Library Reference . Im bardziej go znasz, tym szybciej znajdziesz odpowiedzi na takie pytania. Szczególnie ważne jest kilka pierwszych rozdziałów na temat wbudowanych obiektów i typów.

Zaletą typu Xrange jest to, że obiekt Xrange zawsze zajmuje tę samą ilość pamięci, bez względu na rozmiar reprezentowanego przez niego zakresu. Nie ma stałych korzyści w zakresie wydajności.

Innym sposobem na znalezienie szybkiej informacji o konstrukcji Pythona jest docstring i funkcja pomocy:

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

1
Biblioteka jest dobra, ale nie zawsze tak łatwo jest uzyskać odpowiedź na zadane pytanie.
Teifion

2
Przejdź do biblioteki, naciśnij ctrl + f, wyszukaj zakres, a otrzymasz dwa wyniki. Odpowiedź na to pytanie nie jest trudna.
David Locke

1
Odwołanie do biblioteki nie działa. Czy możesz to zaktualizować?
mk ..

14

Jestem zszokowany, nikt nie czytał dokumentu :

Ta funkcja jest bardzo podobna do range(), ale zwraca xrangeobiekt zamiast listy. Jest to nieprzezroczysty typ sekwencji, który daje takie same wartości jak odpowiadająca mu lista, bez faktycznego przechowywania ich wszystkich jednocześnie. Przewaga xrange()over range()jest minimalna (ponieważ xrange()wciąż trzeba tworzyć wartości, gdy zostanie o to poproszony), z wyjątkiem sytuacji, gdy na maszynie pozbawionej pamięci używany jest bardzo duży zakres lub gdy wszystkie elementy zakresu nie są nigdy używane (na przykład gdy pętla jest zwykle zakończone break).


13

zakres tworzy listę, więc jeśli wykonasz zakres (1, 10000000), tworzona jest lista pamięci zawierająca 10000000 elementów. xrange jest generatorem, więc ocenia leniwie.

Daje to dwie zalety:

  1. Możesz iterować dłuższe listy bez uzyskiwania MemoryError.
  2. Ponieważ leniwie rozwiązuje każdy numer, jeśli wcześniej przerwiesz iterację, nie będziesz tracić czasu na tworzenie całej listy.

12

Zaletę xrangeponad znajdziesz rangew tym prostym przykładzie:

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

Powyższy przykład nie odzwierciedla niczego znacznie lepszego w przypadku xrange.

Teraz spójrz na następujący przypadek, w którym rangejest naprawdę bardzo wolny, w porównaniu do 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

Za pomocą rangetworzy już listę od 0 do 100000000 (czasochłonną), ale xrangejest generatorem i generuje liczby tylko na podstawie potrzeby, to znaczy, jeśli iteracja będzie kontynuowana.

W Pythonie-3 implementacja rangefunkcjonalności jest taka sama jak xrangew Pythonie-2, podczas gdy zlikwidowano xrangew Pythonie-3

Happy Coding !!


11

To z powodów optymalizacji.

range () utworzy listę wartości od początku do końca (w twoim przykładzie 0 .. 20). To stanie się kosztowną operacją na bardzo dużych zasięgach.

Z drugiej strony xrange () jest znacznie bardziej zoptymalizowany. oblicza następną wartość tylko w razie potrzeby (za pomocą obiektu sekwencji Xrange) i nie tworzy listy wszystkich wartości, jak robi to zakres ().


9

range(x,y)zwraca listę każdej liczby pomiędzy xiy, jeśli używasz forpętli, wtedy rangejest wolniejsza. W rzeczywistości rangema większy zakres indeksu. range(x.y)wypisze listę wszystkich liczb pomiędzy xiy

xrange(x,y)zwraca, xrange(x,y)ale jeśli użyłeś forpętli, to xrangejest szybszy. xrangema mniejszy zakres indeksu. xrangenie tylko wydrukuje, xrange(x,y)ale zachowa wszystkie zawarte w nim liczby.

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

Jeśli użyjesz forpętli, to zadziała

[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

Nie ma dużej różnicy przy korzystaniu z pętli, choć jest różnica tylko podczas drukowania!


8

range (): range (1, 10) zwraca listę od 1 do 10 liczb i utrzymuje całą listę w pamięci.

xrange (): Podobnie jak range (), ale zamiast zwracać listę, zwraca obiekt, który na żądanie generuje liczby w zakresie. W przypadku zapętlania jest to nieco szybsze niż range () i bardziej wydajna pamięć. Obiekt xrange () jak iterator i generuje liczby na żądanie. (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

6

Niektóre inne odpowiedzi wspominają, że Python 3 wyeliminował 2.x rangei zmienił nazwę 2.x xrangena range. Jednakże, chyba że używasz wersji 3.0 lub 3.1 (którymi nikt nie powinien być), to w rzeczywistości jest to nieco inny typ.

Jak mówią dokumenty 3.1 :

Obiekty zakresu mają bardzo małe zachowanie: obsługują tylko indeksowanie, iterację i lenfunkcję.

Jednak w wersji 3.2+ rangejest to pełna sekwencja - obsługuje rozszerzone wycinki i wszystkie metody collections.abc.Sequencez tą samą semantyką jak list. *

I, przynajmniej w CPython i pypy (jedyne dwa 3.2 lub nowszym wdrożeń, które obecnie istnieją), ma też implementacje stałej porze indexi countmetod oraz inoperatora (tak długo, jak tylko go przekazywać liczby całkowite). Oznacza to, że pisanie 123456 in rjest rozsądne w wersji 3.2+, podczas gdy w wersji 2.7 lub 3.1 byłby to okropny pomysł.


* Fakt, że issubclass(xrange, collections.Sequence)powraca Truew wersjach 2.6-2.7 i 3.0-3.1, jest błędem, który został naprawiony w wersji 3.2 i nie został przeniesiony.


6

W python 2.x

zakres (x) zwraca listę utworzoną w pamięci za pomocą x elementów.

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

xrange (x) zwraca obiekt xrange, który jest generatorem obj, który generuje liczby na żądanie. są obliczane podczas pętli for (Lazy Evaluation).

W przypadku zapętlania jest to nieco szybsze niż zakres () i bardziej wydajna pamięć.

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

xrange()nie jest generatorem. xrange(n).__ iter __ () `is.
th3an0maly

5

Podczas testowania zasięgu względem xrange w pętli (wiem, że powinienem użyć timeit , ale szybko zhackowałem go z pamięci za pomocą prostego przykładu ze zrozumieniem listy) znalazłem:

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)

co daje:

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

Lub używając xrange w pętli 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

Czy testowanie mojego fragmentu jest prawidłowe? Jakieś komentarze na temat wolniejszej instancji Xrange? Lub lepszy przykład :-)


2
Jednorazowe przeprowadzenie takiego testu porównawczego nie zapewnia dokładnych wyników pomiaru czasu. Zawsze istnieje wariancja. Może to być GC lub inny proces kradnący procesor ... cokolwiek. Właśnie dlatego testy porównawcze są zwykle przeprowadzane w zakresie 10-100-1000 -...
Vajk Hermecz,

to tylko pośpieszny wydruk fragmentu - uruchomiłem go kilka razy, ale tylko do około 100, i xrangewydawałem się nieco szybszy, chociaż w Pythonie 3 porównanie jest teraz zbędne.
Dave Everitt

3
Po to timeitjest. Zajmuje się bieganiem wiele razy, wyłączaniem GC, używaniem najlepszego zegara zamiast timeitp.
abarnert

4

xrange () i range () w pythonie działają podobnie jak dla użytkownika, ale różnica pojawia się, gdy mówimy o tym, jak pamięć jest przydzielana przy użyciu obu funkcji.

Kiedy używamy range (), przydzielamy pamięć dla wszystkich generowanych zmiennych, więc nie zaleca się używania z większym nie. zmiennych do wygenerowania.

Z drugiej strony xrange () generuje tylko określoną wartość na raz i może być używana tylko z pętlą for do drukowania wszystkich wymaganych wartości.


3

zakres generuje całą listę i zwraca ją. xrange nie - generuje na żądanie liczby z listy.


2

xrange używa iteratora (generuje wartości w locie), zakres zwraca listę.


2

Co?
rangezwraca statyczną listę w czasie wykonywania.
xrangezwraca object(który działa jak generator, choć na pewno nie jest to jeden), z którego wartości są generowane w razie potrzeby.

Kiedy stosować który?

  • Posługiwać się xrange jeśli chcesz wygenerować listę dla gigantycznego zasięgu, powiedzmy 1 miliard, zwłaszcza gdy masz „system wrażliwy na pamięć”, taki jak telefon komórkowy.
  • Użyj, rangejeśli chcesz kilkakrotnie iterować listę.

PS: Python 3.x jest rangefunkcja == Python 2.x jest xrangefunkcja.


xrangenie zwraca obiektu generatora.
abarnert

Jeśli dobrze rozumiem, tak to wyjaśniono tutaj (dla Python 2.x): wiki.python.org/moin/Generators
kmario23

Zatem wiki jest błędna. (Nie wiem, kto jest „SH”, kto dodał i podpisał ten komentarz.) Oficjalna dokumentacja jest poprawna; możesz to przetestować sam i sprawdzić, czy jest to generator, czy sekwencja.
abarnert

ok. Ale nadal jest mylące po przeczytaniu tego: stackoverflow.com/questions/135041/...
kmario23

1
Zabawne pytanie brzmi: co zrobić, gdy tłumacz nie zgadza się z oficjalnymi dokumentami lub z innym tłumaczem… Ale na szczęście nie zdarza się to zbyt często…
abarnert

2

Wszyscy to doskonale wyjaśnili. Ale chciałem to zobaczyć na własne oczy. Używam python3. Otworzyłem więc monitor zasobów (w systemie Windows!) I najpierw wykonałem następujące polecenie:

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

a następnie sprawdził zmianę w pamięci „W użyciu”. To było nieistotne. Następnie uruchomiłem następujący kod:

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

Natychmiast zajęło to sporo pamięci. I byłem przekonany. Możesz spróbować sam.

Jeśli używasz Python 2X, zamień „range ()” na „xrange ()” w pierwszym kodzie, a „list (range ())” na „range ()”.


2

Z dokumentacji pomocy.

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

Różnica jest widoczna. W Pythonie 2.x rangezwraca listę, xrangezwraca obiekt xrange, który jest iterowalny.

W Python 3.x rangestaje się xrangePython 2.x i xrangejest usuwany.


1

W przypadku wymagań dotyczących skanowania / drukowania elementów 0-N zakres i zakres X działają w następujący sposób.

range () - tworzy nową listę w pamięci i przenosi całe 0 do N elementów (całkowicie N + 1) i drukuje je. xrange () - tworzy instancję iteratora, która skanuje elementy i przechowuje tylko bieżący napotkany element w pamięci, a zatem wykorzystuje tę samą ilość pamięci przez cały czas.

W przypadku, gdy wymagany element znajduje się nieco na początku listy, oszczędza to sporo czasu i pamięci.


1
xrangenie tworzy instancji iteratora. Tworzy xrangeobiekt, który jest iterowalny, ale nie jest iteratorem - prawie (ale nie do końca) sekwencją, jak lista.
abarnert

1

Range zwraca listę, podczas gdy xrange zwraca obiekt xrange, który zajmuje tę samą pamięć niezależnie od wielkości zakresu, ponieważ w tym przypadku generowany jest tylko jeden element i dostępny na iterację, natomiast w przypadku użycia zakresu wszystkie elementy są generowane jednocześnie i są dostępne w pamięci.


1

Różnica maleje w przypadku mniejszych argumentów do 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

W tym przypadku xrange(100)jest tylko około 20% bardziej wydajny.


1

zakres: -zakres zapełni wszystko naraz. co oznacza, że ​​każda liczba zakresu zajmie pamięć.

xrange: -xrange jest czymś w rodzaju generatora, pojawi się na obrazie, gdy chcesz zakres liczb, ale nie chcesz, aby były przechowywane, na przykład gdy chcesz użyć pętli. tak wydajna pamięć.


1

Dodatkowo, jeśli do list(xrange(...))będzie równoważne range(...).

Więc listjest wolny.

Również xrangenaprawdę nie w pełni kończy sekwencję

Dlatego to nie jest lista, to xrangeobiekt


1

range() w Pythonie 2.x

Ta funkcja jest zasadniczo starą range()funkcją dostępną w Pythonie 2.xi zwraca instancję listobiektu zawierającego elementy z określonego zakresu.

Jednak ta implementacja jest zbyt nieefektywna, jeśli chodzi o inicjalizację listy z zakresem liczb. Na przykład for i in range(1000000)wykonanie polecenia byłoby bardzo kosztowne, zarówno pod względem zużycia pamięci, jak i czasu, ponieważ wymaga przechowywania tej listy w pamięci.


range()w Python 3.xi xrange()Python2.x

Python 3.xwprowadził nowszą implementację range()(podczas gdy nowsza implementacja była już dostępna w Pythonie 2.xza pośrednictwemxrange() funkcję).

range()Wykorzystuje strategię zwaną oceny leniwy. Zamiast tworzyć ogromną listę elementów w zakresie, nowsza implementacja wprowadza klasę range, lekki obiekt, który reprezentuje wymagane elementy w danym zakresie, bez przechowywania ich wyraźnie w pamięci (może to brzmieć jak generatory, ale koncepcja leniwej oceny jest różne).


Jako przykład weź pod uwagę następujące kwestie:

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

i

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

-2

Zobacz ten post aby znaleźć różnicę między zasięgiem a zasięgiem xrange:

Cytować:

rangezwraca dokładnie to, co myślisz: listę kolejnych liczb całkowitych o określonej długości rozpoczynającej się od 0. xrangezwraca jednak „obiekt xrange” , który działa bardzo podobnie jak iterator


2
Zdaję sobie sprawę, że ma to 5 lat, ale ten post jest błędny w prawie wszystkim. xrangenie jest iteratorem. Lista zwracana przez rangenie obsługuje iteracji (lista jest w zasadzie prototypowym przykładem iterowalności). Ogólna korzyść xrangenie jest „minimalna”. I tak dalej.
abarnert
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.