Besteht Bedarf an Reichweite (len (a))?

82

Solche Ausdrücke findet man häufig in Python-Fragen zu SO. Entweder für den einfachen Zugriff auf alle Elemente der Iterable

for i in range(len(a)):
    print(a[i])

Welches ist nur eine umständliche Art zu schreiben:

for e in a:
    print(e)

Oder zum Zuweisen zu Elementen des iterablen:

for i in range(len(a)):
    a[i] = a[i] * 2

Welches sollte das gleiche sein wie:

for i, e in enumerate(a):
     a[i] = e * 2
# Or if it isn't too expensive to create a new iterable
a = [e * 2 for e in a]

Oder zum Filtern über die Indizes:

for i in range(len(a)):
    if i % 2 == 1: continue
    print(a[i])

Was so ausgedrückt werden könnte:

for e in a [::2]:
    print(e)

Oder wenn Sie nur die Länge der Liste und nicht deren Inhalt benötigen:

for _ in range(len(a)):
    doSomethingUnrelatedToA()

Welches könnte sein:

for _ in a:
    doSomethingUnrelatedToA()

In Python wir haben enumerate, schneiden, filter, sortedusw ... Als Python - forKonstrukte zu iterieren Iterables vorgesehen sind und nicht reicht nur von ganzen Zahlen gibt es reale Anwendungsfälle , wo Sie brauchen in range(len(a))?

Hyperboreus
quelle
5
Ich denke, range(len(a))es sind normalerweise Leute, die mit Python ziemlich unerfahren sind (obwohl nicht unbedingt mit Programmierung im Allgemeinen).
rlms
Ich habe es nur verwendet, range(len(a))als ich Python gelernt habe. Heutzutage nicht, weil es, wie Sie sagten, ziemlich einfach zu ersetzen ist.
nicht wirklich. Ich benutze range(len(a))oft, weil ich nicht den Inhalt von Liste a brauche, sondern nur die Länge.
aIKid
8
Was ist, wenn ich in der Schleife vor und nach dem aktuellen auf das Element zugreifen muss? Ich habe normalerweise for i in range(len(a)): doSomethingAbout(a[i+1] - a[i])Wie kann ich das umgehen?
Zhang18
1
@ JaakkoSeppälä stimmte zu. Ich habe nur ein Beispiel gegeben, um das Hauptproblem zu veranschaulichen, Indizes durchlaufen zu müssen, nicht nur Werte, und zu verstehen, dass es am Ende einen Eckfall gibt, der neben dem Hauptpunkt liegt.
Zhang18

Antworten:

17

Wenn Sie mit Indizes einer Sequenz arbeiten müssen, dann ja - Sie verwenden es ... zB für das Äquivalent von numpy.argsort ...:

>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
Jon Clements
quelle
OK, das sieht vernünftig aus. Vielen Dank. Die Frage ist jedoch: Was werden Sie mit Ihrer neu sortierten Liste von Indizes tun? Wenn Sie über diese Liste erneut auf einige iterable zugreifen, beißt der Hund seinen eigenen Schwanz.
Hyperboreus
1
Entspricht: [ix for ix, _ in sorted(enumerate(a), key=lambda i: i[1])]obwohl, obwohl Ihre wohl netter / geekier ist.
Erik Kaplun
10

Was ist, wenn Sie gleichzeitig auf zwei Elemente der Liste zugreifen müssen?

for i in range(len(a[0:-1])):
    something_new[i] = a[i] * a[i+1]

Sie können dies verwenden, aber es ist wahrscheinlich weniger klar:

for i, _ in enumerate(a[0:-1]):
     something_new[i] = a[i] * a[i+1]

Persönlich bin ich auch nicht 100% zufrieden!

Giswok
quelle
1
for ix, i in enumerate(a)scheint gleichwertig zu sein, nein?
Erik Kaplun
1
Man sollte stattdessen paarweise verwenden.
fliegende Schafe
In solchen Situationen mache ich:for a1,a2 in zip(a[:-1],a[1:])
Luca Amerio
7

Kurze Antwort : mathematisch gesehen nein, praktisch ja, zum Beispiel für die absichtliche Programmierung.

Technisch wäre die Antwort "Nein, es wird nicht benötigt", da es mit anderen Konstrukten ausgedrückt werden kann. In der Praxis verwende ich for i in range(len(a)(oder for _ in range(len(a))wenn ich den Index nicht benötige), um deutlich zu machen, dass ich so oft iterieren möchte, wie Elemente in einer Sequenz vorhanden sind, ohne die Elemente in der Sequenz für irgendetwas verwenden zu müssen.

Also: "Gibt es einen Bedarf?" ? - Ja, ich brauche es, um die Bedeutung / Absicht des Codes für Lesbarkeitszwecke auszudrücken.

Siehe auch: https://en.wikipedia.org/wiki/Intentional_programming

Und wenn es überhaupt keine Sammlung gibt, die mit der Iteration verknüpft ist, ist dies natürlich for ... in range(len(N))die einzige Option, um nicht darauf zurückzugreifeni = 0; while i < N; i += 1 ...

Erik Kaplun
quelle
Welche Vorteile hat for _ in range(len(a))gegenüber for _ in a?
Hyperboreus
@Hyperboreus: Ja, ich habe meine Antwort nur ein paar Sekunden vor Ihrem Kommentar geändert. Ich denke, der Unterschied besteht darin, ob Sie wirklich explizit über "WIE VIELE MAL wiederholen" möchten, da es afür jeden aaElemente in " im Gegensatz zu " gibt Element in , unabhängig vom Inhalt von " ... also nur eine Nuance der absichtlichen Programmierung.
Erik Kaplun
Vielen Dank für Ihr Beispiel. Ich habe es in meine Frage aufgenommen.
Hyperboreus
2
Um eine Liste 'hello'mit so vielen Elementen wie in der Liste zu erhalten a, verwenden Sieb = ['hello'] * len(a)
steabert
2

Anhand der Kommentare und der persönlichen Erfahrung sage ich nein, es besteht keine Notwendigkeit dafür range(len(a)). Alles, was Sie tun range(len(a))können, kann auf eine andere (normalerweise weitaus effizientere) Weise erfolgen.

Sie haben in Ihrem Beitrag viele Beispiele angegeben, daher werde ich sie hier nicht wiederholen. Stattdessen werde ich ein Beispiel für diejenigen geben, die sagen: "Was ist, wenn ich nur die Länge aund nicht die Elemente möchte ?". Dies ist eines der wenigen Male, die Sie in Betracht ziehen könnten range(len(a)). Aber auch dies kann folgendermaßen geschehen:

>>> a = [1, 2, 3, 4]
>>> for _ in a:
...     print True
...
True
True
True
True
>>>

Clements Antwort (wie von Allik gezeigt) kann auch überarbeitet werden, um Folgendes zu entfernen range(len(a)):

>>> a = [6, 3, 1, 2, 5, 4]
>>> sorted(range(len(a)), key=a.__getitem__)
[2, 3, 1, 5, 4, 0]
>>> # Note however that, in this case, range(len(a)) is more efficient.
>>> [x for x, _ in sorted(enumerate(a), key=lambda i: i[1])]
[2, 3, 1, 5, 4, 0]
>>>

Zusammenfassend range(len(a))ist also nicht erforderlich . Der einzige Vorteil ist die Lesbarkeit (die Absicht ist klar). Aber das ist nur Präferenz und Codestil.


quelle
Vielen Dank. Andererseits liegt die Lesbarkeit (teilweise) im Auge des Betrachters. Ich interpretiere for _ in a:als "Iterieren über a, ignoriere aber dessen Inhalt", aber ich interpretiere for _ in range(len(a))als "Holen Sie sich die Länge von a, erstellen Sie dann eine Anzahl von ganzen Zahlen derselben Länge und ignorieren Sie schließlich den Inhalt".
Hyperboreus
1
@ Hyperboreus - Sehr wahr. Es ist nur Code-Stil. Mein Ziel war es zu zeigen, dass es niemals ein range(len(a))Szenario geben wird, in dem ich dies verwenden muss oder nicht kann.
Nebenbei bemerkt: ZB in Erlang ist der einzelne Unterstrich die anonyme Variable. Es ist die einzige Variable, die im Gegensatz zu anderen Variablen neu zugewiesen (oder "angepasst") werden kann, da erlang keine destruktive Zuweisung zulässt (was im Allgemeinen ein Greuel ist und den Schleier zwischen uns und den Unterbereichen schwächt, in denen HE wartet hinter der Mauer in seinem Palast aus gefoltertem Glas).
Hyperboreus
2

Manchmal erfordert matplotlib range(len(y)), z. B. während y=array([1,2,5,6]), plot(y)funktioniert gut, scatter(y)nicht. Man muss schreiben scatter(range(len(y)),y). (Ich persönlich denke, dies ist ein Fehler in scatter; plotund seinen Freunden scatterund stemsollte die gleichen Aufrufsequenzen so oft wie möglich verwenden.)

Charles Boncelet
quelle
2

Es ist schön zu haben, wenn Sie den Index für irgendeine Art von Manipulation verwenden müssen und das aktuelle Element nicht ausreicht. Nehmen Sie zum Beispiel einen Binärbaum, der in einem Array gespeichert ist. Wenn Sie eine Methode haben, die Sie auffordert, eine Liste von Tupeln zurückzugeben, die die direkten untergeordneten Knoten jedes Knotens enthält, benötigen Sie den Index.

#0 -> 1,2 : 1 -> 3,4 : 2 -> 5,6 : 3 -> 7,8 ...
nodes = [0,1,2,3,4,5,6,7,8,9,10]
children = []
for i in range(len(nodes)):
  leftNode = None
  rightNode = None
  if i*2 + 1 < len(nodes):
    leftNode = nodes[i*2 + 1]
  if i*2 + 2 < len(nodes):
    rightNode = nodes[i*2 + 2]
  children.append((leftNode,rightNode))
return children

Wenn das Element, an dem Sie arbeiten, ein Objekt ist, können Sie natürlich einfach eine get-children-Methode aufrufen. Aber ja, Sie brauchen den Index nur dann wirklich, wenn Sie irgendeine Art von Manipulation durchführen.

CleoR
quelle
1

Ich habe einen Anwendungsfall, von dem ich glaube, dass keines Ihrer Beispiele ihn abdeckt.

boxes = [b1, b2, b3]
items = [i1, i2, i3, i4, i5]
for j in range(len(boxes)):
    boxes[j].putitemin(items[j])

Ich bin relativ neu in Python, obwohl ich so glücklich bin, einen eleganteren Ansatz zu lernen.

Jim
quelle
4
Meine Unwissenheit. Es gibt zip, eine viel pythonischere Art, über 2 Listen parallel zu iterieren.
Jim
1
Hah, ich bin mit einem wirklich ähnlichen Anwendungsfall hierher gekommen ... [a - b for a, b in zip(list1, list2)]ist so viel schöner als [list1[i] - list2[i] for i in range(len(list1))]... Danke!
Kevlarr
1

Wenn Sie die ersten len(a)Elemente eines Objekts durchlaufen müssen b(das ist größer als a), sollten Sie wahrscheinlich Folgendes verwenden range(len(a)):

for i in range(len(a)):
    do_something_with(b[i])
Alexpirin
quelle
2
Dies könnte klarer sein:for b_elem in b[:len(a)]:...
Aquirdturtle
@aquirdturtle Vielleicht ist es klarer, aber Ihre Lösung erstellt eine neue Liste, die teuer sein kann, wenn B & A groß sind.
PM 2Ring
Dies sollte stattdessen mit behandelt werden itertools.islice.
MisterMiyagi
1

Manchmal interessiert Sie die Sammlung selbst wirklich nicht . Erstellen Sie beispielsweise eine einfache Modellanpassungslinie, um eine "Annäherung" mit den Rohdaten zu vergleichen:

fib_raw = [1, 1, 2, 3, 5, 8, 13, 21] # Fibonacci numbers

phi = (1 + sqrt(5)) / 2
phi2 = (1 - sqrt(5)) / 2

def fib_approx(n): return (phi**n - phi2**n) / sqrt(5)

x = range(len(data))
y = [fib_approx(n) for n in x]

# Now plot to compare fib_raw and y
# Compare error, etc

In diesem Fall waren die Werte der Fibonacci-Sequenz selbst irrelevant. Wir brauchten hier nur die Größe der Eingabesequenz, mit der wir verglichen haben.

Mateen Ulhaq
quelle
Ich bin neu in Python. Was machen die ** in diesem Fall? Ich habe über * args und ** kwargs gelesen, aber das sieht anders aus.
lukas_o
1
Potenzierung. Phi zur Potenz von n.
Mateen Ulhaq
0

Sehr einfaches Beispiel:

def loadById(self, id):
    if id in range(len(self.itemList)):
        self.load(self.itemList[id])

Ich kann mir keine Lösung vorstellen, bei der die Range-Len-Zusammensetzung nicht schnell verwendet wird.

Aber wahrscheinlich sollte dies stattdessen getan werden try .. except, um pythonisch zu bleiben, denke ich.

IARI
quelle
1
if id < len(self.itemList) Ist try...exceptaber besser, wie du sagst.
Saulspatz
Dies berücksichtigt nicht ID <0.
IARI
0

Mein Code lautet:

s=["9"]*int(input())
for I in range(len(s)):
    while not set(s[I])<=set('01'):s[i]=input(i)
print(bin(sum([int(x,2)for x in s]))[2:])

Es ist ein binärer Addierer, aber ich denke nicht, dass der Bereich len oder das Innere ersetzt werden kann, um ihn kleiner / besser zu machen.

Matt GSM MattGSM
quelle