Czy ktoś może krótko opisać różnicę między 2 dla nas, którzy nie są pythonami? Może coś w stylu „xrange () robi wszystko, co robi zakres (), ale obsługuje także X, Y i Z”
programista Outlaw Programmer
87
zakres (n) tworzy listę zawierającą wszystkie liczby całkowite 0..n-1. Jest to problem, jeśli wykonasz zasięg (1000000), ponieważ skończysz z listą> 4 Mb. xrange radzi sobie z tym, zwracając obiekt, który udaje listę, ale po prostu oblicza liczbę potrzebną z żądanego indeksu i zwraca to.
Zasadniczo, podczas gdy range(1000)jest list, xrange(1000)jest obiektem, który działa jak generator(choć na pewno nie jest jednym). Jest też xrangeszybszy. Możesz, import timeit from timeita następnie stworzyć metodę, która właśnie ma for i in xrange: passinną metodę, a rangenastępnie wykonaj timeit(method1)i timeit(method2), a oto, xrange jest czasami prawie dwa razy szybszy (wtedy, gdy nie potrzebujesz listy). (Dla mnie, dla i in xrange(1000):passvs dla i in range(1000):passzajęło odpowiednio 13.316725969314575vs 21.190124988555908sekundy - to dużo.)
Wydajność, szczególnie w przypadku iteracji w dużym zakresie, xrange()jest zwykle lepsza. Jednak nadal istnieje kilka przypadków, dla których możesz preferować range():
W Pythonie 3 range()robi to , co xrange()kiedyś i xrange()nie istnieje. Jeśli chcesz napisać kod, który będzie działał zarówno w Pythonie 2, jak i Pythonie 3, nie możesz go użyć xrange().
range()w niektórych przypadkach może być szybszy - np. jeśli iteruje się wielokrotnie w tej samej sekwencji. xrange()musi za każdym razem rekonstruować obiekt liczb całkowitych, ale range()będzie miał rzeczywiste obiekty liczb całkowitych. (Zawsze jednak będzie gorzej pod względem pamięci)
xrange()nie jest użyteczny we wszystkich przypadkach, w których potrzebna jest prawdziwa lista. Na przykład nie obsługuje plasterków ani żadnych metod list.
[Edytuj] Istnieje kilka postów range()mówiących o tym , jak zostanie zaktualizowane narzędzie 2to3. Dla przypomnienia, oto wyniki działania narzędzia na niektórych przykładowych zastosowaniach range()ixrange()
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)
Jak widać, w przypadku użycia w pętli for lub zrozumieniu, lub gdy jest już zawinięty za pomocą list (), zakres pozostaje niezmieniony.
Co rozumiesz przez „zakres stanie się iteratorem”? Czy nie powinien to być „generator”?
Michael Mior,
4
Nie. Generator odnosi się do określonego typu iteratora, a nowy i rangetak nie jest iteratorem.
użytkownik2357112 obsługuje Monikę
Twoja druga kula naprawdę nie ma sensu. Mówisz, że nie możesz użyć obiektu wiele razy; oczywiście że możesz! spróbuj xr = xrange(1,11)w następnej linii for i in xr: print " ".join(format(i*j,"3d") for j in xr)i voila! Masz swoje tabele czasu do dziesięciu. Działa tak samo jak r = range(1,11)i for i in r: print " ".join(format(i*j,"3d") for j in r)... wszystko w Python2 jest obiektem. Myślę, że chciałeś powiedzieć, że możesz lepiej rozumieć na podstawie indeksu (jeśli ma to sens) rangew przeciwieństwie do xrange. Zasięg jest przydatny bardzo rzadko, jak sądzę
dylnmc
(Nie wystarczająco dużo miejsca) Chociaż ja zrobić myślę, że rangemoże być przydatny, jeśli chcesz używać listw pętli, a następnie zmienić niektóre indeksy oparte na określonych warunków lub rzeczy dołącza do tej listy, to rangejest zdecydowanie lepiej. Jednak,xrange po prostu szybszy i zużywa mniej pamięci, więc dla większości aplikacji pętlowych wydaje się najlepszy. Są przypadki - wracając do pytania pytającego - rzadko, ale istnieją, gdzie rangebyłoby lepiej. Być może nie tak rzadko, jak myślę, ale z pewnością używam xrange95% czasu.
dylnmc
129
Nie, oba mają swoje zastosowania:
Użyj xrange()podczas iteracji, ponieważ oszczędza pamięć. Mówić:
for x in xrange(1, one_zillion):
zamiast:
for x in range(1, one_zillion):
Z drugiej strony, użyj, range()jeśli naprawdę chcesz listę liczb.
multiples_of_seven = range(7,100,7)print"Multiples of seven < 100: ", multiples_of_seven
Należy sprzyjać range()ciągu xrange()tylko kiedy trzeba rzeczywistą listę. Na przykład, gdy chcesz zmodyfikować listę zwróconą przez range()lub chcesz ją pokroić. W przypadku iteracji lub nawet zwykłego indeksowania xrange()będzie działał dobrze (i zwykle znacznie wydajniej). Jest moment, w którym range()jest nieco szybszy niż w xrange()przypadku bardzo małych list, ale w zależności od sprzętu i różnych innych szczegółów, próg rentowności może być wynikiem długości 1 lub 2; nie ma się czym martwić. Wolą xrange().
Inną różnicą jest to, że xrange () nie może obsługiwać liczb większych niż C ints, więc jeśli chcesz mieć zakres za pomocą wbudowanej obsługi dużej liczby Pythona, musisz użyć range ().
Python2.7.3(default,Jul132012,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 nie ma tego problemu:
Python3.2.3(default,Jul142012,01:01:48)[GCC 4.7.1] on linux2
Type"help","copyright","credits"or"license"for more information.>>> range(123456787676676767676676,123456787676676767676679)
range(123456787676676767676676,123456787676676767676679)
xrange()jest bardziej wydajny, ponieważ zamiast generować listę obiektów, generuje tylko jeden obiekt na raz. Zamiast 100 liczb całkowitych i wszystkich ich kosztów ogólnych oraz listy do umieszczenia w nich, masz tylko jedną liczbę całkowitą na raz. Szybsze generowanie, lepsze wykorzystanie pamięci, bardziej wydajny kod.
O ile nie potrzebuję konkretnej listy do czegoś, zawsze faworyzuję xrange()
range () zwraca listę, xrange () zwraca obiekt xrange.
xrange () jest nieco szybszy i nieco bardziej wydajny pod względem pamięci. Ale zysk nie jest bardzo duży.
Dodatkowa pamięć używana przez listę jest oczywiście nie tylko marnowana, listy mają więcej funkcji (wycinanie, powtarzanie, wstawianie, ...). Dokładne różnice można znaleźć w dokumentacji . Nie ma reguły twardej kości, użyj tego, co jest potrzebne.
Python 3.0 jest wciąż w fazie rozwoju, ale range () IIRC będzie bardzo podobny do xrange () 2.X i list (range ()) można użyć do generowania list.
Chciałbym tylko powiedzieć, że NAPRAWDĘ nie jest tak trudno uzyskać obiekt Xrange z funkcją wycinania i indeksowania. Napisałem trochę kodu, który działa całkiem nieźle i jest tak samo szybki jak xrange, gdy się liczy (iteracje).
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
classXrange(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 objectsif 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,Noneif 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:raiseValueError(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 !=Noneelse0# convert start = None to start = 0if start <0:
start += start
start = self[start]if start <0:raiseIndexError(item)
step =(self.step if self.step !=Noneelse1)*(step if step !=Noneelse1)
stop = stop if stop isnotNoneelse self.xrange[-1]if stop <0:
stop += stop
stop = self[stop]
stop = stop
if stop > self.stop:raiseIndexErrorif start < self.start:raiseIndexErrorreturnXrange(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)
Szczerze mówiąc, myślę, że cała sprawa jest trochę głupia i xrange powinien to wszystko zrobić ...
Tak, zgodził się; z zupełnie innej technologii, sprawdź pracę na lodash, aby go lenić : github.com/lodash/lodash/issues/274 . Krojenie itp. Powinno nadal być tak leniwe, jak to możliwe, a jeśli nie, to tylko wtedy, gdy to nastąpi.
Rob Grant,
4
Dobry przykład podany w książce: Practical Python Autor: Magnus Lie Hetland
W poprzednim przykładzie nie zalecałbym używania zakresu zamiast zakresu x - chociaż potrzebne są tylko pierwsze pięć liczb, zakres oblicza wszystkie liczby, co może zająć dużo czasu. W przypadku Xrange nie stanowi to problemu, ponieważ oblicza tylko te liczby, które są potrzebne.
Tak, przeczytałem odpowiedź Briana: W Pythonie 3, range () jest zresztą generatorem, a xrange () nie istnieje.
Nie rób tego xrange () zniknie, ale i wiele innych rzeczy. Narzędzie, którego będziesz używać do tłumaczenia kodu Python 2.x na kod Python 3.x, automatycznie przetłumaczy xrange () na range (), ale range () zostanie przetłumaczony na mniej wydajną listę (range ()).
Thomas Wouters
10
Thomas: W rzeczywistości jest nieco mądrzejszy. Przetłumaczy range () w sytuacjach, w których wyraźnie nie potrzebuje prawdziwej listy (np. W pętli for lub zrozumieniu), do zwykłego range (). Jedyne przypadki, gdzie zostanie przypisana do zmiennej, lub bezpośrednio stosowane muszą być opakowane z listy ()
Brian
2
Okej, wszyscy tutaj mają inne zdanie na temat kompromisów i zalet Xrange w porównaniu z zasięgiem. Są w większości poprawne, xrange jest iteratorem, a zakres rozwija się i tworzy rzeczywistą listę. W większości przypadków tak naprawdę nie zauważysz różnicy między nimi. (Możesz używać mapy z zasięgiem, ale nie z Xrange, ale zajmuje więcej pamięci.)
Myślę jednak, że chcesz usłyszeć, że preferowanym wyborem jest xrange. Ponieważ zakres w Pythonie 3 jest iteratorem, narzędzie do konwersji kodu 2to3 poprawnie skonwertuje wszystkie zastosowania xrange na zakres i wyrzuci błąd lub ostrzeżenie dotyczące użycia zakresu. Jeśli chcesz w przyszłości łatwo przekonwertować kod, użyjesz tylko xrange i list (xrange), gdy masz pewność, że chcesz mieć listę. Nauczyłem się tego podczas sprintu CPython w PyCon w tym roku (2008) w Chicago.
To nieprawda. Kod typu „dla x w zakresie (20)” pozostanie jako zakres, a kod typu „x = zakres (20)” zostanie przekonwertowany na „x = lista (zakres (20))” - bez błędów. Ponadto, jeśli chcesz napisać kod, który będzie działał zarówno w wersjach 2.6, jak i 3.0, range () jest Twoją jedyną opcją bez dodawania funkcji kompatybilności.
Brian
2
range(): range(1, 10)zwraca listę od 1 do 10 cyfr i utrzymuje całą listę w pamięci.
xrange(): Podobnie range(), ale zamiast zwracać listę, zwraca obiekt, który na żądanie generuje liczby w zakresie. W przypadku zapętlania jest to nieco szybsze range()i bardziej wydajne pod względem pamięci.xrange()obiekt jak iterator i generuje liczby na żądanie (Lazy Evaluation).
range()robi to samo, co xrange()w Pythonie 3, aw Pythonie 3 nie xrange()istnieje
termin. range()W niektórych sytuacjach może być szybszy, jeśli będziesz powtarzał tę samą sekwencję wiele razy. xrange()musi za każdym razem rekonstruować obiekt liczb całkowitych, ale range()będzie miał rzeczywiste obiekty liczb całkowitych.
Chociaż xrangejest szybszy niż rangew większości przypadków, różnica w wydajności jest dość minimalna. Mały program poniżej porównuje iterację z a rangei an 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 resultprint"Loop list of len %d: range=%.4f, xrange=%.4f"%(list_len, rtime, xrtime)
Poniższe wyniki pokazują, że xrangejest rzeczywiście szybszy, ale niewystarczający, aby się pocić.
Loop list of len 1: range=0.0003, xrange=0.0003Loop list of len 10: range=0.0013, xrange=0.0011Loop list of len 100: range=0.0068, xrange=0.0034Loop list of len 1000: range=0.0609, xrange=0.0438Loop list of len 10000: range=0.5527, xrange=0.5266Loop list of len 100000: range=10.1666, xrange=7.8481Loop list of len 1000000: range=168.3425, xrange=155.8719
Więc xrangena pewno używaj , ale jeśli nie masz ograniczonego sprzętu, nie przejmuj się zbytnio.
range(1000)
jestlist
,xrange(1000)
jest obiektem, który działa jakgenerator
(choć na pewno nie jest jednym). Jest teżxrange
szybszy. Możesz,import timeit from timeit
a następnie stworzyć metodę, która właśnie mafor i in xrange: pass
inną metodę, arange
następnie wykonajtimeit(method1)
itimeit(method2)
, a oto, xrange jest czasami prawie dwa razy szybszy (wtedy, gdy nie potrzebujesz listy). (Dla mnie, dlai in xrange(1000):pass
vs dlai in range(1000):pass
zajęło odpowiednio13.316725969314575
vs21.190124988555908
sekundy - to dużo.)xrange(100)
o 20% szybszy niżrange(100)
.Odpowiedzi:
Wydajność, szczególnie w przypadku iteracji w dużym zakresie,
xrange()
jest zwykle lepsza. Jednak nadal istnieje kilka przypadków, dla których możesz preferowaćrange()
:W Pythonie 3
range()
robi to , coxrange()
kiedyś ixrange()
nie istnieje. Jeśli chcesz napisać kod, który będzie działał zarówno w Pythonie 2, jak i Pythonie 3, nie możesz go użyćxrange()
.range()
w niektórych przypadkach może być szybszy - np. jeśli iteruje się wielokrotnie w tej samej sekwencji.xrange()
musi za każdym razem rekonstruować obiekt liczb całkowitych, alerange()
będzie miał rzeczywiste obiekty liczb całkowitych. (Zawsze jednak będzie gorzej pod względem pamięci)xrange()
nie jest użyteczny we wszystkich przypadkach, w których potrzebna jest prawdziwa lista. Na przykład nie obsługuje plasterków ani żadnych metod list.[Edytuj] Istnieje kilka postów
range()
mówiących o tym , jak zostanie zaktualizowane narzędzie 2to3. Dla przypomnienia, oto wyniki działania narzędzia na niektórych przykładowych zastosowaniachrange()
ixrange()
Jak widać, w przypadku użycia w pętli for lub zrozumieniu, lub gdy jest już zawinięty za pomocą list (), zakres pozostaje niezmieniony.
źródło
range
tak nie jest iteratorem.xr = xrange(1,11)
w następnej liniifor i in xr: print " ".join(format(i*j,"3d") for j in xr)
i voila! Masz swoje tabele czasu do dziesięciu. Działa tak samo jakr = range(1,11)
ifor i in r: print " ".join(format(i*j,"3d") for j in r)
... wszystko w Python2 jest obiektem. Myślę, że chciałeś powiedzieć, że możesz lepiej rozumieć na podstawie indeksu (jeśli ma to sens)range
w przeciwieństwie doxrange
. Zasięg jest przydatny bardzo rzadko, jak sądzęrange
może być przydatny, jeśli chcesz używaćlist
w pętli, a następnie zmienić niektóre indeksy oparte na określonych warunków lub rzeczy dołącza do tej listy, torange
jest zdecydowanie lepiej. Jednak,xrange
po prostu szybszy i zużywa mniej pamięci, więc dla większości aplikacji pętlowych wydaje się najlepszy. Są przypadki - wracając do pytania pytającego - rzadko, ale istnieją, gdzierange
byłoby lepiej. Być może nie tak rzadko, jak myślę, ale z pewnością używamxrange
95% czasu.Nie, oba mają swoje zastosowania:
Użyj
xrange()
podczas iteracji, ponieważ oszczędza pamięć. Mówić:zamiast:
Z drugiej strony, użyj,
range()
jeśli naprawdę chcesz listę liczb.źródło
Należy sprzyjać
range()
ciąguxrange()
tylko kiedy trzeba rzeczywistą listę. Na przykład, gdy chcesz zmodyfikować listę zwróconą przezrange()
lub chcesz ją pokroić. W przypadku iteracji lub nawet zwykłego indeksowaniaxrange()
będzie działał dobrze (i zwykle znacznie wydajniej). Jest moment, w którymrange()
jest nieco szybszy niż wxrange()
przypadku bardzo małych list, ale w zależności od sprzętu i różnych innych szczegółów, próg rentowności może być wynikiem długości 1 lub 2; nie ma się czym martwić. Woląxrange()
.źródło
Inną różnicą jest to, że xrange () nie może obsługiwać liczb większych niż C ints, więc jeśli chcesz mieć zakres za pomocą wbudowanej obsługi dużej liczby Pythona, musisz użyć range ().
Python 3 nie ma tego problemu:
źródło
xrange()
jest bardziej wydajny, ponieważ zamiast generować listę obiektów, generuje tylko jeden obiekt na raz. Zamiast 100 liczb całkowitych i wszystkich ich kosztów ogólnych oraz listy do umieszczenia w nich, masz tylko jedną liczbę całkowitą na raz. Szybsze generowanie, lepsze wykorzystanie pamięci, bardziej wydajny kod.O ile nie potrzebuję konkretnej listy do czegoś, zawsze faworyzuję
xrange()
źródło
range () zwraca listę, xrange () zwraca obiekt xrange.
xrange () jest nieco szybszy i nieco bardziej wydajny pod względem pamięci. Ale zysk nie jest bardzo duży.
Dodatkowa pamięć używana przez listę jest oczywiście nie tylko marnowana, listy mają więcej funkcji (wycinanie, powtarzanie, wstawianie, ...). Dokładne różnice można znaleźć w dokumentacji . Nie ma reguły twardej kości, użyj tego, co jest potrzebne.
Python 3.0 jest wciąż w fazie rozwoju, ale range () IIRC będzie bardzo podobny do xrange () 2.X i list (range ()) można użyć do generowania list.
źródło
Chciałbym tylko powiedzieć, że NAPRAWDĘ nie jest tak trudno uzyskać obiekt Xrange z funkcją wycinania i indeksowania. Napisałem trochę kodu, który działa całkiem nieźle i jest tak samo szybki jak xrange, gdy się liczy (iteracje).
Szczerze mówiąc, myślę, że cała sprawa jest trochę głupia i xrange powinien to wszystko zrobić ...
źródło
Dobry przykład podany w książce: Practical Python Autor: Magnus Lie Hetland
W poprzednim przykładzie nie zalecałbym używania zakresu zamiast zakresu x - chociaż potrzebne są tylko pierwsze pięć liczb, zakres oblicza wszystkie liczby, co może zająć dużo czasu. W przypadku Xrange nie stanowi to problemu, ponieważ oblicza tylko te liczby, które są potrzebne.
Tak, przeczytałem odpowiedź Briana: W Pythonie 3, range () jest zresztą generatorem, a xrange () nie istnieje.
źródło
Wybierz zakres z następujących powodów:
1) xrange zniknie w nowszych wersjach Pythona. Zapewnia to łatwą kompatybilność w przyszłości.
2) zakres nabierze wydajności związanych z xrange.
źródło
Okej, wszyscy tutaj mają inne zdanie na temat kompromisów i zalet Xrange w porównaniu z zasięgiem. Są w większości poprawne, xrange jest iteratorem, a zakres rozwija się i tworzy rzeczywistą listę. W większości przypadków tak naprawdę nie zauważysz różnicy między nimi. (Możesz używać mapy z zasięgiem, ale nie z Xrange, ale zajmuje więcej pamięci.)
Myślę jednak, że chcesz usłyszeć, że preferowanym wyborem jest xrange. Ponieważ zakres w Pythonie 3 jest iteratorem, narzędzie do konwersji kodu 2to3 poprawnie skonwertuje wszystkie zastosowania xrange na zakres i wyrzuci błąd lub ostrzeżenie dotyczące użycia zakresu. Jeśli chcesz w przyszłości łatwo przekonwertować kod, użyjesz tylko xrange i list (xrange), gdy masz pewność, że chcesz mieć listę. Nauczyłem się tego podczas sprintu CPython w PyCon w tym roku (2008) w Chicago.
źródło
range()
:range(1, 10)
zwraca listę od 1 do 10 cyfr i utrzymuje całą listę w pamięci.xrange()
: Podobnierange()
, ale zamiast zwracać listę, zwraca obiekt, który na żądanie generuje liczby w zakresie. W przypadku zapętlania jest to nieco szybszerange()
i bardziej wydajne pod względem pamięci.xrange()
obiekt jak iterator i generuje liczby na żądanie (Lazy Evaluation).range()
robi to samo, coxrange()
w Pythonie 3, aw Pythonie 3 niexrange()
istnieje termin.range()
W niektórych sytuacjach może być szybszy, jeśli będziesz powtarzał tę samą sekwencję wiele razy.xrange()
musi za każdym razem rekonstruować obiekt liczb całkowitych, alerange()
będzie miał rzeczywiste obiekty liczb całkowitych.źródło
Chociaż
xrange
jest szybszy niżrange
w większości przypadków, różnica w wydajności jest dość minimalna. Mały program poniżej porównuje iterację z arange
i anxrange
:Poniższe wyniki pokazują, że
xrange
jest rzeczywiście szybszy, ale niewystarczający, aby się pocić.Więc
xrange
na pewno używaj , ale jeśli nie masz ograniczonego sprzętu, nie przejmuj się zbytnio.źródło
list_len
nie jest używany, więc uruchamiasz ten kod tylko dla list o długości 100.rtime = timeit.timeit('a=0;\nfor n in range(%d): a += n' % list_len, number=1000)