Grundsätzlich wählen Sie einen Satz von einzelnen Fehlern für einen anderen aus. Ein Satz führt eher dazu, dass Ihre Schleifen vorzeitig beendet werden, der andere verursacht wahrscheinlich eine Ausnahme (oder einen Pufferüberlauf in anderen Sprachen). Sobald Sie eine Reihe von Code geschrieben haben, werden Sie feststellen, dass die Wahl des Verhaltens range()viel häufiger Sinn macht
@unutbu Dieser Djikstra-Artikel wird in diesem Thema oft zitiert, bietet aber hier nichts Wertvolles. Die Leute benutzen ihn nur als Appell an die Autorität. Der einzige relevante Pseudo-Grund, den er für die Frage des OP angibt, ist, dass er das Gefühl hat, dass das Einbeziehen der Obergrenze in dem speziellen Fall, in dem die Sequenz leer ist, "unnatürlich" und "hässlich" wird - das ist eine völlig subjektive Position und leicht zu argumentieren, es bringt also nicht viel auf den Tisch. Das "Experiment" mit Mesa ist auch ohne Kenntnis ihrer besonderen Einschränkungen oder Bewertungsmethoden nicht von großem Wert.
Sundar - Reinstate Monica
6
@andreasdr Aber selbst wenn das kosmetische Argument gültig ist, führt Pythons Ansatz nicht zu einem neuen Problem der Lesbarkeit? Im gebräuchlichen Englisch impliziert der Begriff "Bereich", dass etwas von etwas zu etwas reicht - wie ein Intervall. Dass len (Liste (Bereich (1,2))) 1 und len (Liste (Bereich (2))) 2 zurückgibt, muss man wirklich lernen zu verdauen.
Armin
Antworten:
245
Weil es üblicher ist, aufzurufen, range(0, 10)welche Rückgabe [0,1,2,3,4,5,6,7,8,9]10 Elemente enthält, die gleich sindlen(range(0, 10)) . Denken Sie daran, dass Programmierer eine 0-basierte Indizierung bevorzugen.
Beachten Sie auch das folgende allgemeine Code-Snippet:
for i in range(len(li)):pass
Könnten Sie sehen, dass dies problematisch wäre , wenn range()genau len(li)das erreicht würde? Der Programmierer müßte explizit subtrahiert 1. Dies auch die gemeinsame Entwicklung von Programmierern folgt lieber for(int i = 0; i < 10; i++)überfor(int i = 0; i <= 9; i++) .
Wenn Sie häufig den Bereich mit einem Start von 1 aufrufen, möchten Sie möglicherweise Ihre eigene Funktion definieren:
Wenn das die Begründung wäre, wären die Parameter nicht range(start, count)?
Mark Ransom
3
@shogun Der Startwert ist standardmäßig 0, dh range(10)entspricht range(0, 10).
Moinudin
4
Sie arbeiten range1nicht mit Bereichen, die eine andere Schrittgröße als haben 1.
dimo414
6
Sie erklären, dass der Bereich (x) mit 0 beginnen sollte und x die "Länge des Bereichs" ist. OK. Sie haben jedoch nicht erklärt, warum der Bereich (x, y) mit x beginnen und mit y-1 enden sollte. Wenn der Programmierer eine for-Schleife mit i im Bereich von 1 bis 3 möchte, muss er explizit 1 hinzufügen. Geht es hier wirklich um Bequemlichkeit?
Armin
7
for i in range(len(li)):ist eher ein Antimuster. Man sollte verwenden enumerate.
Hans
27
Obwohl es hier einige nützliche algorithmische Erklärungen gibt, denke ich, dass es hilfreich sein kann, einige einfache Argumente aus dem wirklichen Leben hinzuzufügen, warum dies so funktioniert, die ich als nützlich empfunden habe, als ich jungen Neulingen das Thema vorstellte:
Bei so etwas wie "Bereich (1,10)" kann Verwirrung entstehen, wenn man denkt, dass ein Parameterpaar den "Anfang und das Ende" darstellt.
Es ist eigentlich Start und "Stop".
Wenn es nun der "End" -Wert wäre, könnten Sie erwarten, dass diese Zahl als letzter Eintrag in die Sequenz aufgenommen wird. Aber es ist nicht das "Ende".
Andere nennen diesen Parameter fälschlicherweise "count", denn wenn Sie immer nur 'range (n)' verwenden, wird er natürlich 'n' Mal wiederholt. Diese Logik bricht zusammen, wenn Sie den Startparameter hinzufügen.
Der wichtigste Punkt ist also, sich an den Namen zu erinnern: " stop ". Dies bedeutet, dass an diesem Punkt die Iteration sofort beendet wird, wenn sie erreicht ist. Nicht danach diesem Punkt.
Während "Start" tatsächlich den ersten Wert darstellt, der eingeschlossen werden soll, "bricht" es beim Erreichen des "Stopp" -Werts, anstatt "auch diesen" vor dem Stoppen weiter zu verarbeiten.
Eine Analogie, die ich verwendet habe, um Kindern dies zu erklären, ist, dass es sich ironischerweise besser benimmt als Kinder! Es hört nicht auf, nachdem es sollte - es hört sofort auf, ohne zu beenden, was es tat. (Sie bekommen das;))
Eine andere Analogie - wenn Sie ein Auto fahren, kommen Sie nicht vorbei Stopp- / Ertrags- / Nachgiebigkeitsschild und landen irgendwo neben oder hinter Ihrem Auto. Technisch gesehen haben Sie es immer noch nicht erreicht, wenn Sie aufhören. Es ist nicht in den "Dingen, die Sie auf Ihrer Reise passiert haben" enthalten.
Ich hoffe, einiges davon hilft bei der Erklärung von Pythonitos / Pythonitas!
Sie versuchen, einem Schwein Lippenstift zu geben. Die Unterscheidung zwischen "Stopp" und "Ende" ist absurd. Wenn ich von 1 auf 7 gehe, habe ich 7 nicht bestanden. Es ist einfach ein Fehler von Python, unterschiedliche Konventionen für Start- und Stopppositionen zu haben. In anderen Sprachen, einschließlich der menschlichen, bedeutet "von X nach Y" "von X nach Y". In Python bedeutet "X: Y" "X: Y-1". Wenn Sie ein Meeting von 9 bis 11 haben, sagen Sie den Leuten, dass es von 9 bis 12 oder von 8 bis 11 ist?
bzip2
24
Exklusive Sortimente haben einige Vorteile:
Zum einen ist jedes Element range(0,n)ein gültiger Index für Längenlisten n.
Hat range(0,n)auch eine Länge von n, nicht n+1welche ein inklusiver Bereich würde.
Es funktioniert gut in Kombination mit nullbasierter Indizierung und len(). Wenn Sie beispielsweise 10 Elemente in einer Liste haben x, sind diese mit 0 bis 9 nummeriert. range(len(x))gibt dir 0-9.
Natürlich werden die Leute sagen , es ist mehr Pythonic zu tun for item in xoder for index, item in enumerate(x)nicht for i in range(len(x)).
Das Schneiden funktioniert auch so: foo[1:4]ist die Elemente 1-3 von foo(wobei zu beachten ist, dass Element 1 aufgrund der nullbasierten Indizierung tatsächlich das zweite Element ist). Aus Gründen der Konsistenz sollten beide gleich funktionieren.
Ich stelle es mir vor als: "Die erste Nummer, die Sie wollen, gefolgt von der ersten Nummer, die Sie nicht wollen." Wenn Sie 1-10 wollen, ist die erste Zahl, die Sie nicht wollen, 11, also ist es range(1, 11).
Wenn es in einer bestimmten Anwendung umständlich wird, ist es einfach genug, eine kleine Hilfsfunktion zu schreiben, die dem Endindex und den Aufrufen 1 hinzufügt range().
Stimmen Sie dem Schneiden zu. 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
Vielleicht sollte das Aufzählungsbeispiel lauten for index, item in enumerate(x), um Verwirrung zu vermeiden
Sean
@seans Danke, behoben.
Kindall
12
Es ist auch nützlich, um Bereiche aufzuteilen. range(a,b)kann in range(a, x)und aufgeteilt werden range(x, b), während Sie mit inklusivem Bereich entweder x-1oder schreiben würden x+1. Während Sie Bereiche selten teilen müssen, neigen Sie dazu, Listen ziemlich oft zu teilen. Dies ist einer der Gründe, warum das Schneiden einer Liste l[a:b]das a-te Element enthält, nicht jedoch das b-te. Dann rangemacht es die gleiche Eigenschaft schön konsistent.
Gah! Ich benutze Ruby - nicht, aber ich kann mir vorstellen , dass 1..10vs 1...10schwer sein , zu unterscheiden , beim Lesen Code!
Moinudin
6
@ Marcog - wenn Sie wissen, dass die beiden Formen existieren, stimmen sich Ihre Augen auf den Unterschied ein :)
Skilldrick
11
Rubys Range Operator ist vollkommen intuitiv. Je länger die Form, desto kürzer die Sequenz. Husten
Russell Borogove
4
@Russell, vielleicht sollte 1 ............ 20 den gleichen Bereich wie 1..10 ergeben. Das wäre ein syntaktischer Zucker, für den es sich zu wechseln lohnt. ;)
Kevpie
4
@ Russell Der zusätzliche Punkt drückt den letzten Gegenstand aus dem Bereich :)
Skilldrick
5
Grundsätzlich können wir in Python- range(n)Iterationszeiten n, die exklusiver Natur sind, keinen letzten Wert angeben, wenn sie gedruckt werden. Wir können eine Funktion erstellen, die einen Inklusivwert angibt. Dies bedeutet, dass auch der zuletzt im Bereich genannte Wert gedruckt wird.
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()
In vielen Fällen ist es einfach bequemer, darüber nachzudenken.
Grundsätzlich können wir uns einen Bereich als Intervall zwischen startund vorstellen end. Wenn start <= end, ist die Länge des Intervalls zwischen ihnen end - start. Wenn lentatsächlich als Länge definiert wurde, hätten Sie:
len(range(start, end))== start - end
Wir zählen jedoch die im Bereich enthaltenen Ganzzahlen, anstatt die Länge des Intervalls zu messen. Um die obige Eigenschaft beizubehalten, sollten wir einen der Endpunkte einschließen und den anderen ausschließen.
Das Hinzufügen des stepParameters entspricht dem Einfügen einer Längeneinheit. In diesem Fall würden Sie erwarten
len(range(start, end, step))==(start - end)/ step
für die Länge. Um die Zählung zu erhalten, verwenden Sie einfach die Ganzzahldivision.
Diese Abwehrmechanismen gegen Pythons Inkonsistenz sind komisch. Wenn ich das Intervall zwischen zwei Zahlen haben wollte, warum sollte ich dann die Subtraktion verwenden, um die Differenz anstelle des Intervalls zu erhalten? Es ist inkonsistent, unterschiedliche Indexierungskonventionen für Start- und Endpositionen zu verwenden. Warum sollten Sie "5:22" schreiben müssen, um die Positionen 5 bis 21 zu erhalten?
bzip2
Es ist nicht Pythons, es ist auf der ganzen Linie ziemlich verbreitet. In C, Java, Ruby nennen Sie es
Arseny
Ich wollte damit sagen, dass es für die Indizierung üblich ist, nicht dass die anderen Sprachen notwendigerweise genau die gleiche Art von Objekt haben
range()
viel häufiger Sinn machtAntworten:
Weil es üblicher ist, aufzurufen,
range(0, 10)
welche Rückgabe[0,1,2,3,4,5,6,7,8,9]
10 Elemente enthält, die gleich sindlen(range(0, 10))
. Denken Sie daran, dass Programmierer eine 0-basierte Indizierung bevorzugen.Beachten Sie auch das folgende allgemeine Code-Snippet:
Könnten Sie sehen, dass dies problematisch wäre , wenn
range()
genaulen(li)
das erreicht würde? Der Programmierer müßte explizit subtrahiert 1. Dies auch die gemeinsame Entwicklung von Programmierern folgt lieberfor(int i = 0; i < 10; i++)
überfor(int i = 0; i <= 9; i++)
.Wenn Sie häufig den Bereich mit einem Start von 1 aufrufen, möchten Sie möglicherweise Ihre eigene Funktion definieren:
quelle
range(start, count)
?range(10)
entsprichtrange(0, 10)
.range1
nicht mit Bereichen, die eine andere Schrittgröße als haben1
.for i in range(len(li)):
ist eher ein Antimuster. Man sollte verwendenenumerate
.Obwohl es hier einige nützliche algorithmische Erklärungen gibt, denke ich, dass es hilfreich sein kann, einige einfache Argumente aus dem wirklichen Leben hinzuzufügen, warum dies so funktioniert, die ich als nützlich empfunden habe, als ich jungen Neulingen das Thema vorstellte:
Bei so etwas wie "Bereich (1,10)" kann Verwirrung entstehen, wenn man denkt, dass ein Parameterpaar den "Anfang und das Ende" darstellt.
Es ist eigentlich Start und "Stop".
Wenn es nun der "End" -Wert wäre, könnten Sie erwarten, dass diese Zahl als letzter Eintrag in die Sequenz aufgenommen wird. Aber es ist nicht das "Ende".
Andere nennen diesen Parameter fälschlicherweise "count", denn wenn Sie immer nur 'range (n)' verwenden, wird er natürlich 'n' Mal wiederholt. Diese Logik bricht zusammen, wenn Sie den Startparameter hinzufügen.
Der wichtigste Punkt ist also, sich an den Namen zu erinnern: " stop ". Dies bedeutet, dass an diesem Punkt die Iteration sofort beendet wird, wenn sie erreicht ist. Nicht danach diesem Punkt.
Während "Start" tatsächlich den ersten Wert darstellt, der eingeschlossen werden soll, "bricht" es beim Erreichen des "Stopp" -Werts, anstatt "auch diesen" vor dem Stoppen weiter zu verarbeiten.
Eine Analogie, die ich verwendet habe, um Kindern dies zu erklären, ist, dass es sich ironischerweise besser benimmt als Kinder! Es hört nicht auf, nachdem es sollte - es hört sofort auf, ohne zu beenden, was es tat. (Sie bekommen das;))
Eine andere Analogie - wenn Sie ein Auto fahren, kommen Sie nicht vorbei Stopp- / Ertrags- / Nachgiebigkeitsschild und landen irgendwo neben oder hinter Ihrem Auto. Technisch gesehen haben Sie es immer noch nicht erreicht, wenn Sie aufhören. Es ist nicht in den "Dingen, die Sie auf Ihrer Reise passiert haben" enthalten.
Ich hoffe, einiges davon hilft bei der Erklärung von Pythonitos / Pythonitas!
quelle
Exklusive Sortimente haben einige Vorteile:
Zum einen ist jedes Element
range(0,n)
ein gültiger Index für Längenlistenn
.Hat
range(0,n)
auch eine Länge vonn
, nichtn+1
welche ein inklusiver Bereich würde.quelle
Es funktioniert gut in Kombination mit nullbasierter Indizierung und
len()
. Wenn Sie beispielsweise 10 Elemente in einer Liste habenx
, sind diese mit 0 bis 9 nummeriert.range(len(x))
gibt dir 0-9.Natürlich werden die Leute sagen , es ist mehr Pythonic zu tun
for item in x
oderfor index, item in enumerate(x)
nichtfor i in range(len(x))
.Das Schneiden funktioniert auch so:
foo[1:4]
ist die Elemente 1-3 vonfoo
(wobei zu beachten ist, dass Element 1 aufgrund der nullbasierten Indizierung tatsächlich das zweite Element ist). Aus Gründen der Konsistenz sollten beide gleich funktionieren.Ich stelle es mir vor als: "Die erste Nummer, die Sie wollen, gefolgt von der ersten Nummer, die Sie nicht wollen." Wenn Sie 1-10 wollen, ist die erste Zahl, die Sie nicht wollen, 11, also ist es
range(1, 11)
.Wenn es in einer bestimmten Anwendung umständlich wird, ist es einfach genug, eine kleine Hilfsfunktion zu schreiben, die dem Endindex und den Aufrufen 1 hinzufügt
range()
.quelle
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)
, um Verwirrung zu vermeidenEs ist auch nützlich, um Bereiche aufzuteilen.
range(a,b)
kann inrange(a, x)
und aufgeteilt werdenrange(x, b)
, während Sie mit inklusivem Bereich entwederx-1
oder schreiben würdenx+1
. Während Sie Bereiche selten teilen müssen, neigen Sie dazu, Listen ziemlich oft zu teilen. Dies ist einer der Gründe, warum das Schneiden einer Listel[a:b]
das a-te Element enthält, nicht jedoch das b-te. Dannrange
macht es die gleiche Eigenschaft schön konsistent.quelle
Die Länge des Bereichs ist der obere Wert minus der untere Wert.
Es ist sehr ähnlich zu etwas wie:
in einer Sprache im C-Stil.
Auch wie Rubys Sortiment:
Ruby erkennt jedoch, dass Sie den Terminalwert häufig einschließen möchten, und bietet die alternative Syntax:
quelle
1..10
vs1...10
schwer sein , zu unterscheiden , beim Lesen Code!Grundsätzlich können wir in Python-
range(n)
Iterationszeitenn
, die exklusiver Natur sind, keinen letzten Wert angeben, wenn sie gedruckt werden. Wir können eine Funktion erstellen, die einen Inklusivwert angibt. Dies bedeutet, dass auch der zuletzt im Bereich genannte Wert gedruckt wird.quelle
Betrachten Sie den Code
Die Idee ist, dass Sie eine Liste der Länge erhalten
y-x
, die Sie (wie Sie oben sehen) durchlaufen können.Informieren Sie sich in den Python-Dokumenten über den Bereich - sie betrachten die For-Loop-Iteration als primären Verwendungszweck.
quelle
In vielen Fällen ist es einfach bequemer, darüber nachzudenken.
Grundsätzlich können wir uns einen Bereich als Intervall zwischen
start
und vorstellenend
. Wennstart <= end
, ist die Länge des Intervalls zwischen ihnenend - start
. Wennlen
tatsächlich als Länge definiert wurde, hätten Sie:Wir zählen jedoch die im Bereich enthaltenen Ganzzahlen, anstatt die Länge des Intervalls zu messen. Um die obige Eigenschaft beizubehalten, sollten wir einen der Endpunkte einschließen und den anderen ausschließen.
Das Hinzufügen des
step
Parameters entspricht dem Einfügen einer Längeneinheit. In diesem Fall würden Sie erwartenfür die Länge. Um die Zählung zu erhalten, verwenden Sie einfach die Ganzzahldivision.
quelle