webentwicklung-frage-antwort-db.com.de

Gibt es ein Bedürfnis nach Reichweite (len (a))?

Ausdrücke dieses Typs findet man häufig in Python-Fragen zu SO. Entweder für den Zugriff auf alle Elemente des Iterierbaren

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

Welches ist nur eine unbeholfene Schreibweise:

for e in a:
    print(e)

Oder für die Zuordnung zu Elementen des Iterierbaren:

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 könnte man so ausdrücken:

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()

Was könnte sein:

for _ in a:
    doSomethingUnrelatedToA()

In Python haben wir enumerate, slicing, filter, sorted, etc ... Da Python for-Konstrukte iterable iterieren sollen und nicht nur Bereiche von ganzen Zahlen, gibt es Anwendungsfälle in der realen Welt, in denen Sie in range(len(a)) brauchen?

54
Hyperboreus

Wenn Sie mit Indizes einer Sequenz arbeiten müssen, dann ja - Sie verwenden sie ... 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]
8
Jon Clements

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

Technisch gesehen wäre die mathematisch korrekte Antwort "nein, es wird nicht benötigt", weil es mit anderen Konstrukten ausgedrückt werden kann, dh es ist äquivalent zu anderen Konstrukten ... so etwas wie wenn eine Sprache vollständig ist Es spielt keine Rolle, welche syntaktischen/paradigmatischen Konstrukte es hat, weil sowieso alles darin ausgedrückt werden kann.

In der Praxis verwende ich jedoch for i in range(len(a) (oder for _ in range(len(a)), wenn ich den Index nicht benötige), um zu verdeutlichen, dass ich so oft iterieren möchte, wie Elemente in einer Sequenz vorhanden sind, ohne sie verwenden zu müssen die Elemente in der Sequenz für alles.

Zur Beantwortung des "Gibt es ein Bedürfnis?" Teils - Ich brauche es, um die Bedeutung/Absicht des Codes für auszudrücken Lesbarkeitszwecke.

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

P.S. aber gleichzeitig scheint das Folgende aus Sicht der absichtlichen Programmierung semantisch äquivalent zu sein:

for _ in a:
    ...

oder

b = ["hello" for _ in a]

... Alles in allem denke ich, ist der Unterschied, ob Sie wirklich explizit sein wollen über "Wiederholen Sie so viele Male, wie es Elemente in a" im Gegensatz zu "für jedes Element in a, unabhängig vom Inhalt von a" ... also am Ende nur eine Nuance der absichtlichen Programmierung.

6
Erik Allik

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]

Ich persönlich bin mit beiden nicht zu 100% zufrieden!

5
Giswok

Ich habe einen Anwendungsfall, von dem ich glaube, dass keines Ihrer Beispiele davon 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.

2
Jim

Manchmal benötigt matplotlib range(len(y)), z. B. während y=array([1,2,5,6]), plot(y) gut funktioniert, scatter(y) nicht. Man muss scatter(range(len(y)),y) schreiben. (Ich persönlich denke, das ist ein Fehler in scatter; plot und seine Freunde scatter und stem sollten möglichst dieselbe Aufrufsequenz verwenden.)

2

Nach den Kommentaren sowie persönlichen Erfahrungen sage ich nein, es gibt keine Notwendigkeit für range(len(a)). Alles, was Sie mit range(len(a)) machen können, kann auf eine andere (normalerweise weitaus effizientere) Weise ausgeführt werden.

Sie haben in Ihrem Beitrag viele Beispiele genannt, 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 von a und nicht die Elemente haben möchte?". Dies ist einer der wenigen Fälle, in denen Sie range(len(a)) in Betracht ziehen könnten. Aber auch das kann so gemacht werden:

>>> 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 range(len(a)) zu entfernen:

>>> 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 wird range(len(a)) nicht benötigt . Ihr einziger Vorteil ist die Lesbarkeit (die Absicht ist klar). Das ist aber nur Präferenz und Code-Stil.

2
iCodez

Es ist schön zu haben, wenn Sie den Index für eine Manipulation verwenden müssen und das aktuelle Element nicht ausreicht. Nehmen Sie zum Beispiel einen binären Baum, 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 eine get-Kind-Methode aufrufen. Aber ja, Sie brauchen den Index wirklich nur, wenn Sie irgendeine Art von Manipulation vornehmen.

2
CleoR

Manchmal sind Sie wirklich kümmert sich nicht um die Sammlung selbst. Erstellen Sie zum Beispiel eine einfache Modellpasslinie, um eine "Nä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. Alles, was wir hier brauchten, war die Größe der Eingabesequenz, mit der wir verglichen wurden.

1
Mateen Ulhaq

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 es kleiner/besser zu machen.

0

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

for i in range(len(a)):
    do_something_with(b[i])
0
alexpirine

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, die die Range-len-Komposition nicht schnell nutzt.

Aber stattdessen sollte dies mit try .. except geschehen, um Pythonic zu bleiben, denke ich.

0
IARI