Zasadniczo wybierasz jeden zestaw błędów jeden po drugim dla drugiego. Jeden zestaw może powodować wcześniejsze zakończenie pętli, drugi może spowodować wyjątek (lub przepełnienie bufora w innych językach). Po napisaniu zestawu kodu zobaczysz, że wybór zachowania range()ma sens znacznie częściej
@unutbu Ten artykuł Djikstry jest często cytowany w tym temacie, ale nie ma tu nic wartościowego, ludzie używają go jako odwołania do autorytetu. Jedynym istotnym pseudo-powodem, dla którego podaje pytanie OP, jest to, że czuje, że włączenie górnej granicy staje się „nienaturalne” i „brzydkie” w szczególnym przypadku, gdy sekwencja jest pusta - jest to całkowicie subiektywna pozycja, z którą łatwo się sprzeciwić, więc nie wnosi wiele do stołu. „Eksperyment” z Mesą nie ma zbyt dużej wartości bez znajomości ich szczególnych ograniczeń lub metod oceny.
Sundar - Przywróć Monikę
6
@andreasdr Ale nawet jeśli argument kosmetyczny jest prawidłowy, czy podejście Pythona nie wprowadza nowego problemu czytelności? W powszechnym języku angielskim termin „zakres” oznacza, że coś sięga od czegoś do czegoś - na przykład interwału. Że len (lista (zakres (1,2))) zwraca 1, a len (lista (zakres (2))) zwraca 2, to jest coś, czego naprawdę musisz nauczyć się trawić.
armin
Odpowiedzi:
245
Ponieważ częstsze jest wywoływanie, range(0, 10)które zwraca [0,1,2,3,4,5,6,7,8,9]10 elementów, które są równelen(range(0, 10)) . Pamiętaj, że programiści wolą indeksowanie oparte na 0.
Weź również pod uwagę następujący wspólny fragment kodu:
for i in range(len(li)):pass
Czy widzisz, że gdyby range()doszło dokładnie do len(li)tego, byłoby to problematyczne? Programista musiałby wyraźnie odjąć 1. Wynika to również wspólny trend programistów preferując for(int i = 0; i < 10; i++)nad for(int i = 0; i <= 9; i++).
Jeśli często wywołujesz zakres z początkiem 1, możesz chcieć zdefiniować własną funkcję:
Gdyby takie było uzasadnienie, czy parametry nie byłyby takie range(start, count)?
Mark Ransom,
3
@shogun Domyślna wartość początkowa to 0, czyli range(10)jest równa range(0, 10).
moinudin
4
Twoje range1nie będzie działać z zakresami, które mają inny rozmiar kroku niż 1.
dimo414
6
Wyjaśniasz, że zakres (x) powinien zaczynać się od 0, a x będzie „długością zakresu”. OK. Ale nie wyjaśniłeś, dlaczego zakres (x, y) powinien zaczynać się od x, a kończyć na y-1. Jeśli programista chce pętli for z i od 1 do 3, musi wyraźnie dodać 1. Czy to naprawdę dotyczy wygody?
armin
7
for i in range(len(li)):jest raczej antypatternem. Należy użyć enumerate.
hans
27
Chociaż istnieją tutaj pewne użyteczne wyjaśnienia algorytmiczne, myślę, że może pomóc dodanie prostego rozumowania w „prawdziwym życiu”, dlaczego działa to w ten sposób, co znalazłem przydatne, gdy przedstawiłem ten temat młodym nowicjuszom:
Przy czymś takim jak „zakres (1,10)” może pojawić się zamieszanie w wyniku myślenia, że para parametrów reprezentuje „początek i koniec”.
W rzeczywistości zaczyna się i „zatrzymuje”.
Teraz gdyby tak było wartości „koniec”, a następnie, tak, możesz spodziewać się, że liczba będzie uwzględniona jako ostatecznego wejścia w sekwencji. Ale to nie jest „koniec”.
Inni błędnie nazywają ten parametr „count”, ponieważ jeśli użyjesz tylko „range (n)”, wówczas oczywiście iteruje się „n” razy. Ta logika ulega awarii po dodaniu parametru początkowego.
Kluczową kwestią jest zapamiętanie jego nazwy: „ stop ”. Oznacza to, że w tym momencie iteracja natychmiast się zatrzyma. Nie po tym punkcie.
Tak więc, chociaż „start” rzeczywiście reprezentuje pierwszą wartość, którą należy uwzględnić, to po osiągnięciu wartości „stop” „pęka”, a nie kontynuuje przetwarzanie „również tej” przed zatrzymaniem.
Jedną z analogii, którą wykorzystałem, tłumacząc to dzieciom, jest to, że, jak na ironię, lepiej się zachowuje niż dzieci! Nie kończy się po tym, jak powinien - zatrzymuje się natychmiast, nie kończąc tego, co robił. (Dostają to;))
Kolejna analogia - kiedy prowadzisz samochód, nie mijamy znaku stop / ustępowania / „ustąp” i kończy się to, że siedzisz gdzieś obok lub za samochodem. Technicznie rzecz biorąc, nadal nie osiągnąłeś tego, kiedy przestałeś. Nie jest uwzględnione w „rzeczach, które przekazałeś podczas podróży”.
Mam nadzieję, że niektóre z nich pomogą wyjaśnić Pythonitos / Pythonitas!
Wyjaśnienie dla dzieci jest po prostu przezabawne!
Antony Hatchkins,
1
Próbujesz nałożyć szminkę na świnię. Rozróżnienie między „stopem” a „końcem” jest absurdalne. Jeśli przejdę od 1 do 7, nie zaliczyłem 7. Po prostu wadą Pythona jest stosowanie różnych konwencji dla pozycji początkowej i końcowej. W innych językach, w tym ludzkich, „od X do Y” oznacza „od X do Y”. W Pythonie „X: Y” oznacza „X: Y-1”. Jeśli masz spotkanie od 9 do 11, czy mówisz ludziom, że od 9 do 12 lub od 8 do 11?
bzip2
24
Ekskluzywne zakresy mają pewne zalety:
Po pierwsze, każdy element range(0,n)jest prawidłowym indeksem dla list długościn .
Również range(0,n)ma długość n, a nie n+1jakim zakresie integracyjnego będzie.
Działa dobrze w połączeniu z indeksowaniem zerowym i len(). Na przykład, jeśli masz 10 pozycji na liście x, są one ponumerowane 0–9. range(len(x))daje ci 0-9.
Oczywiście, ludzie będą wam to bardziej pythonowy zrobić for item in xlub for index, item in enumerate(x)zamiast for i in range(len(x)).
Krojenie również działa w ten sposób: foo[1:4]to pozycje 1-3 foo(pamiętając, że pozycja 1 jest tak naprawdę drugą pozycją ze względu na indeksowanie zerowe). Aby zachować spójność, oba powinny działać w ten sam sposób.
Myślę o tym jako: „pierwszy numer, którego chcesz, a następnie pierwszy numer, którego nie chcesz”. Jeśli chcesz 1-10, pierwszą liczbą, której nie chcesz, jest 11, więc jest range(1, 11).
Jeśli w konkretnej aplikacji staje się nieporęczny, wystarczy napisać małą funkcję pomocniczą, która dodaje 1 do indeksu końcowego i wywołań range().
Zgadzam się na krojenie. w = 'abc'; w[:] == w[0:len(w)]; w[:-1] == w[0:len(w)-1];
kevpie
def full_range(start,stop): return range(start,stop+1) ## helper function
nobar
może wymieniony przykład powinien przeczytać, for index, item in enumerate(x)aby uniknąć zamieszania
seans
@seans Dzięki, naprawiono.
kindall
12
Jest także przydatny do dzielenia zakresów; range(a,b)można podzielić na range(a, x)a range(x, b), natomiast z zakresu integracyjnego byś napisać albo x-1albo x+1. Chociaż rzadko trzeba dzielić zakresy, często dzielisz listy, co jest jednym z powodów, dla których krojenie listy l[a:b]zawiera a-ty element, ale nie b-ty. Zatem rangeposiadanie tej samej właściwości sprawia, że jest ładnie spójna.
Gah! Nie używam Ruby, ale mogę sobie wyobrazić, że 1..10vs 1...10będąc ciężko rozróżnić podczas czytania kodu!
moinudin
6
@marcog - gdy wiesz, że istnieją dwie formy, twoje oczy
dostrzegają
11
Operator zakresu Ruby jest całkowicie intuicyjny. Im dłuższa forma, tym krótsza sekwencja. kaszel
Russell Borogove,
4
@ Russell, może 1 ........... 20 powinien dać taki sam zakres jak 1..10. To byłby cukier syntaktyczny, na który warto się zamienić. ;)
kevpie
4
@ Russell Dodatkowa kropka wyciska ostatni przedmiot spoza zakresu :)
Skilldrick
5
Zasadniczo w pythonie range(n)iteruje się nczasy, które mają charakter wyłączny, dlatego nie podaje ostatniej wartości podczas drukowania, możemy stworzyć funkcję, która daje wartość włączającą, co oznacza, że wypisze również ostatnią wartość wymienioną w zakresie.
def main():for i in inclusive_range(25):print(i, sep=" ")def inclusive_range(*args):
numargs = len(args)if numargs ==0:raiseTypeError("you need to write at least a value")elif numargs ==1:
stop = args[0]
start =0
step =1elif numargs ==2:(start, stop)= args
step =1elif numargs ==3:(start, stop, step)= args
else:raiseTypeError("Inclusive range was expected at most 3 arguments,got {}".format(numargs))
i = start
while i <= stop:yield i
i += step
if __name__ =="__main__":
main()
Zasadniczo moglibyśmy myśleć o zakresie jako odstępie między starti end. Jeśli start <= endodstęp między nimi wynosi end - start. Gdyby lenfaktycznie zdefiniowano jako długość, miałbyś:
len(range(start, end))== start - end
Liczymy jednak liczby całkowite zawarte w zakresie, zamiast mierzyć długość przedziału. Aby powyższa właściwość była prawdziwa, powinniśmy uwzględnić jeden z punktów końcowych, a drugi wykluczyć.
Dodanie stepparametru jest jak wprowadzenie jednostki długości. W takim przypadku można się spodziewać
len(range(start, end, step))==(start - end)/ step
na długość. Aby uzyskać liczbę, wystarczy użyć podziału na liczby całkowite.
Te mechanizmy niespójności Pythona są przezabawne. Gdybym chciał odstępu między dwiema liczbami, dlaczego miałbym używać odejmowania, aby uzyskać różnicę zamiast przedziału? Niespójne jest stosowanie różnych konwencji indeksowania pozycji początkowej i końcowej. Dlaczego trzeba pisać „5:22”, aby uzyskać pozycje od 5 do 21?
bzip2
To nie jest Python, to dość powszechne na całym forum. W C, Java, Ruby, nazywasz to
Arseny
Chciałem powiedzieć, że indeksowanie jest powszechne, a nie, że inne języki muszą mieć dokładnie ten sam rodzaj obiektu
range()
ma sens znacznie częściejOdpowiedzi:
Ponieważ częstsze jest wywoływanie,
range(0, 10)
które zwraca[0,1,2,3,4,5,6,7,8,9]
10 elementów, które są równelen(range(0, 10))
. Pamiętaj, że programiści wolą indeksowanie oparte na 0.Weź również pod uwagę następujący wspólny fragment kodu:
Czy widzisz, że gdyby
range()
doszło dokładnie dolen(li)
tego, byłoby to problematyczne? Programista musiałby wyraźnie odjąć 1. Wynika to również wspólny trend programistów preferującfor(int i = 0; i < 10; i++)
nadfor(int i = 0; i <= 9; i++)
.Jeśli często wywołujesz zakres z początkiem 1, możesz chcieć zdefiniować własną funkcję:
źródło
range(start, count)
?range(10)
jest równarange(0, 10)
.range1
nie będzie działać z zakresami, które mają inny rozmiar kroku niż1
.for i in range(len(li)):
jest raczej antypatternem. Należy użyćenumerate
.Chociaż istnieją tutaj pewne użyteczne wyjaśnienia algorytmiczne, myślę, że może pomóc dodanie prostego rozumowania w „prawdziwym życiu”, dlaczego działa to w ten sposób, co znalazłem przydatne, gdy przedstawiłem ten temat młodym nowicjuszom:
Przy czymś takim jak „zakres (1,10)” może pojawić się zamieszanie w wyniku myślenia, że para parametrów reprezentuje „początek i koniec”.
W rzeczywistości zaczyna się i „zatrzymuje”.
Teraz gdyby tak było wartości „koniec”, a następnie, tak, możesz spodziewać się, że liczba będzie uwzględniona jako ostatecznego wejścia w sekwencji. Ale to nie jest „koniec”.
Inni błędnie nazywają ten parametr „count”, ponieważ jeśli użyjesz tylko „range (n)”, wówczas oczywiście iteruje się „n” razy. Ta logika ulega awarii po dodaniu parametru początkowego.
Kluczową kwestią jest zapamiętanie jego nazwy: „ stop ”. Oznacza to, że w tym momencie iteracja natychmiast się zatrzyma. Nie po tym punkcie.
Tak więc, chociaż „start” rzeczywiście reprezentuje pierwszą wartość, którą należy uwzględnić, to po osiągnięciu wartości „stop” „pęka”, a nie kontynuuje przetwarzanie „również tej” przed zatrzymaniem.
Jedną z analogii, którą wykorzystałem, tłumacząc to dzieciom, jest to, że, jak na ironię, lepiej się zachowuje niż dzieci! Nie kończy się po tym, jak powinien - zatrzymuje się natychmiast, nie kończąc tego, co robił. (Dostają to;))
Kolejna analogia - kiedy prowadzisz samochód, nie mijamy znaku stop / ustępowania / „ustąp” i kończy się to, że siedzisz gdzieś obok lub za samochodem. Technicznie rzecz biorąc, nadal nie osiągnąłeś tego, kiedy przestałeś. Nie jest uwzględnione w „rzeczach, które przekazałeś podczas podróży”.
Mam nadzieję, że niektóre z nich pomogą wyjaśnić Pythonitos / Pythonitas!
źródło
Ekskluzywne zakresy mają pewne zalety:
Po pierwsze, każdy element
range(0,n)
jest prawidłowym indeksem dla list długościn
.Również
range(0,n)
ma długośćn
, a nien+1
jakim zakresie integracyjnego będzie.źródło
Działa dobrze w połączeniu z indeksowaniem zerowym i
len()
. Na przykład, jeśli masz 10 pozycji na liściex
, są one ponumerowane 0–9.range(len(x))
daje ci 0-9.Oczywiście, ludzie będą wam to bardziej pythonowy zrobić
for item in x
lubfor index, item in enumerate(x)
zamiastfor i in range(len(x))
.Krojenie również działa w ten sposób:
foo[1:4]
to pozycje 1-3foo
(pamiętając, że pozycja 1 jest tak naprawdę drugą pozycją ze względu na indeksowanie zerowe). Aby zachować spójność, oba powinny działać w ten sam sposób.Myślę o tym jako: „pierwszy numer, którego chcesz, a następnie pierwszy numer, którego nie chcesz”. Jeśli chcesz 1-10, pierwszą liczbą, której nie chcesz, jest 11, więc jest
range(1, 11)
.Jeśli w konkretnej aplikacji staje się nieporęczny, wystarczy napisać małą funkcję pomocniczą, która dodaje 1 do indeksu końcowego i wywołań
range()
.źródło
w = 'abc'; w[:] == w[0:len(w)]; w[:-1] == w[0:len(w)-1];
def full_range(start,stop): return range(start,stop+1) ## helper function
for index, item in enumerate(x)
aby uniknąć zamieszaniaJest także przydatny do dzielenia zakresów;
range(a,b)
można podzielić narange(a, x)
arange(x, b)
, natomiast z zakresu integracyjnego byś napisać albox-1
albox+1
. Chociaż rzadko trzeba dzielić zakresy, często dzielisz listy, co jest jednym z powodów, dla których krojenie listyl[a:b]
zawiera a-ty element, ale nie b-ty. Zatemrange
posiadanie tej samej właściwości sprawia, że jest ładnie spójna.źródło
Długość zakresu to górna wartość minus dolna wartość.
Jest bardzo podobny do czegoś takiego:
w języku C.
Podobnie jak zakres Ruby:
Jednak Ruby rozpoznaje, że wiele razy będziesz chciał dołączyć wartość końcową i oferuje alternatywną składnię:
źródło
1..10
vs1...10
będąc ciężko rozróżnić podczas czytania kodu!Zasadniczo w pythonie
range(n)
iteruje sięn
czasy, które mają charakter wyłączny, dlatego nie podaje ostatniej wartości podczas drukowania, możemy stworzyć funkcję, która daje wartość włączającą, co oznacza, że wypisze również ostatnią wartość wymienioną w zakresie.źródło
Rozważ kod
Chodzi o to, że otrzymujesz listę długości
y-x
, którą możesz (jak widać powyżej) iterować.Przeczytaj dokumentację Pythona dotyczącą zakresu - uważają oni, że iteracja pętli for jest podstawowym przypadkiem użycia.
źródło
W wielu przypadkach wygodniej jest rozumować.
Zasadniczo moglibyśmy myśleć o zakresie jako odstępie między
start
iend
. Jeślistart <= end
odstęp między nimi wynosiend - start
. Gdybylen
faktycznie zdefiniowano jako długość, miałbyś:Liczymy jednak liczby całkowite zawarte w zakresie, zamiast mierzyć długość przedziału. Aby powyższa właściwość była prawdziwa, powinniśmy uwzględnić jeden z punktów końcowych, a drugi wykluczyć.
Dodanie
step
parametru jest jak wprowadzenie jednostki długości. W takim przypadku można się spodziewaćna długość. Aby uzyskać liczbę, wystarczy użyć podziału na liczby całkowite.
źródło