402 Stimmen

Doppelte Iteration beim Verstehen von Listen

In Python kann man mehrere Iteratoren in einem Listenverständnis haben, wie

[(x,y) for x in a for y in b]

für einige geeignete Sequenzen a und b. Ich bin mir der verschachtelten Schleifensemantik von Pythons Listenauffassungen bewusst.

Meine Frage ist: Kann ein Iterator in der Comprehension auf den anderen verweisen? Mit anderen Worten: Könnte ich etwas wie dieses haben:

[x for x in a for a in b]

wobei der aktuelle Wert der äußeren Schleife der Iterator der inneren Schleife ist?

Wenn ich zum Beispiel eine verschachtelte Liste habe:

a=[[1,2],[3,4]]

Wie würde der Ausdruck für das Listenverständnis lauten, um dieses Ergebnis zu erzielen?

[1,2,3,4]

?? (Bitte geben Sie nur verständliche Antworten an, denn das ist es, was ich herausfinden möchte).

1 Stimmen

Ich weiß nicht, wer diese Frage als Duplikat geschlossen hat einer anderen Frage die 4 Jahre jünger ist als diese. Wenn überhaupt, wäre die andere Frage das Duplikat. Aber ich bezweifle auch, dass sie dasselbe Problem ansprechen. In der anderen Frage geht es speziell um die Umwandlung von Elementen in verschachtelten Listen, in dieser Frage geht es um die internen Abhängigkeiten von mehreren Iteratoren innerhalb von Listenauflösungen. Natürlich muss man erst das eine verstehen, bevor man das andere angehen kann.

419voto

Skam Punkte 6279

Angenommen, Sie haben einen Text mit vielen Sätzen und möchten eine Reihe von Wörtern.

# Without list comprehension
list_of_words = []
for sentence in text:
    for word in sentence:
       list_of_words.append(word)
return list_of_words

Ich betrachte das Verstehen von Listen gerne als horizontale Ausdehnung des Codes.

Versuchen Sie es aufzuteilen in:

# List Comprehension 
[word for sentence in text for word in sentence]

Ejemplo:

>>> text = (("Hi", "Steve!"), ("What's", "up?"))
>>> [word for sentence in text for word in sentence]
['Hi', 'Steve!', "What's", 'up?']

Dies gilt auch für Generatoren

>>> text = (("Hi", "Steve!"), ("What's", "up?"))
>>> gen = (word for sentence in text for word in sentence)
>>> for word in gen: print(word)
Hi
Steve!
What's
up?

39 Stimmen

"Es gibt nur zwei schwierige Probleme in der Informatik: Cache-Invalidierung und die Benennung von Dingen." -- Phil Karlton

2 Stimmen

Das ist eine großartige Antwort, da sie das ganze Problem weniger abstrakt macht! Ich danke Ihnen!

0 Stimmen

Ich habe mich gefragt, ob man das Gleiche mit drei Abstraktionsebenen in einem Listenverständnis machen kann? Wie Kapitel im Text, Sätze in Kapiteln und Wörter in Sätzen?

202voto

André Eriksson Punkte 4148

Um Ihre Frage mit Ihrem eigenen Vorschlag zu beantworten:

>>> [x for b in a for x in b] # Works fine

Da Sie nach Antworten zum Listenverständnis gefragt haben, möchte ich auch auf das ausgezeichnete itertools.chain() hinweisen:

>>> from itertools import chain
>>> list(chain.from_iterable(a))
>>> list(chain(*a)) # If you're using python < 2.6

1 Stimmen

Es ist sogar noch schlimmer: Es vermischt Vorwärts- und Rückwärtsschreibung. Das ist so lächerlich, als würde man 07/04/1776 schreiben, um den vierten Tag des siebten Monats des Jahres 1776 zu bezeichnen.

163voto

ThomasH Punkte 20646

Mensch, ich glaube, ich habe die Antwort gefunden: Ich habe nicht sorgfältig genug darauf geachtet, welche Schleife inner und welche äußer ist. Das Listenverständnis sollte wie folgt sein:

[x for b in a for x in b]

um das gewünschte Ergebnis zu erhalten, und ja, ein aktueller Wert kann der Iterator für die nächste Schleife sein.

92 Stimmen

Die Syntax des Listenverständnisses gehört nicht zu den Glanzlichtern von Python.

4 Stimmen

@Glenn Ja, bei mehr als einfachen Ausdrücken wird es leicht unübersichtlich.

1 Stimmen

Igitt. Ich bin mir nicht sicher, dass dies die "übliche" Verwendung für Liste Auffassungen ist, aber es ist sehr bedauerlich, dass Verkettung so böse in Python ist.

72voto

Dima Tisnek Punkte 10230

Die Reihenfolge der Iteratoren mag kontra-intuitiv erscheinen.

Nehmen Sie zum Beispiel: [str(x) for i in range(3) for x in foo(i)]

Zerlegen wir sie:

def foo(i):
    return i, i + 0.5

[str(x)
    for i in range(3)
        for x in foo(i)
]

# is same as
for i in range(3):
    for x in foo(i):
        yield str(x)

9 Stimmen

Was für ein Augenöffner!

2 Stimmen

Nach meinem Verständnis ist der Grund dafür, dass "die erste aufgeführte Iteration die oberste Iteration ist, die eingegeben würde, wenn das Verständnis als verschachtelte for-Schleifen geschrieben wäre". Der Grund dafür, dass dies kontraintuitiv ist, ist, dass die OUTER-Schleife (die oberste, wenn sie als verschachtelte for-Schleifen geschrieben ist) an der INNEN Seite der eingeklammerten Liste/des Diktats (Objekt des Verstehens) erscheint. Umgekehrt ist die INNER-Schleife (innerste, wenn sie als verschachtelte for-Schleifen geschrieben ist) genau die ganz rechte Schleife in einer Compension und erscheint somit an der AUSSEN-Seite der Compension.

2 Stimmen

Abstrakt geschrieben haben wir [(output in loop 2) (loop 1) (loop 2)] con (loop 1) = for i in range(3) y (loop 2) = for x in foo(i): y (output in loop 2) = str(x) .

29voto

Sławomir Lenart Punkte 6134

Diese Gedächtnistechnik hilft mir sehr:

[ <RETURNED_VALUE> <OUTER_LOOP1> <INNER_LOOP2> <INNER_LOOP3> ... <OPTIONAL_IF> ]

Und jetzt können Sie darüber nachdenken R eturn + O uter-loop als einzige R ight O rder

Mit dem Wissen von oben, die Reihenfolge in der Liste umfassend auch für 3 Schleifen scheinen einfach:


c=[111, 222, 333]
b=[11, 22, 33]
a=[1, 2, 3]

print(
  [
    (i, j, k)                            # <RETURNED_VALUE> 
    for i in a for j in b for k in c     # in order: loop1, loop2, loop3
    if i < 2 and j < 20 and k < 200      # <OPTIONAL_IF>
  ]
)
[(1, 11, 111)]

denn das obige ist nur ein:

for i in a:                         # outer loop1 GOES SECOND
  for j in b:                       # inner loop2 GOES THIRD
    for k in c:                     # inner loop3 GOES FOURTH
      if i < 2 and j < 20 and k < 200:
        print((i, j, k))            # returned value GOES FIRST

für die Iteration einer verschachtelten Liste/Struktur, die Technik ist dieselbe: für a aus der Frage:

a = [[1,2],[3,4]]
[i2    for i1 in a      for i2 in i1]
which return [1, 2, 3, 4]

für eine andere verschachtelte Ebene

a = [[[1, 2], [3, 4]], [[5, 6], [7, 8, 9]], [[10]]]
[i3    for i1 in a      for i2 in i1     for i3 in i2]
which return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

und so weiter

2 Stimmen

Danke, aber was Sie beschreiben, ist eigentlich der einfache Fall, in dem die beteiligten Iteratoren unabhängig sind. In der Tat, in Ihrem Beispiel könnten Sie die Iteratoren verwenden in beliebiger Reihenfolge und würde dieselbe Ergebnisliste erhalten (Modulo-Ordnung). Der Fall, an dem ich mehr interessiert war, war mit verschachtelten Listen, wo ein Iterator die Iterable des nächsten wird.

0 Stimmen

@ThomasH: Die fettgedruckte Reihenfolge der Schleifen entspricht genau Ihrem Bedarf. Unten wurde ein Beispiel hinzugefügt, das Ihre Daten abdeckt, und ein weiteres Beispiel mit einer zusätzlichen verschachtelten Ebene.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X