Ich bin neu in der Python-Programmierung. Ich möchte den folgenden Code als Listenverständnis umschreiben:

lx = [1, 2, 3, 4, 5, 1, 2]
ly = [2, 5, 4]

lz = []
for x in lx:
    if x in ly and x not in lz:
        lz.append(x)

Dadurch wird eine neue Liste mit gemeinsamen Elementen von lx und ly erstellt. Die Bedingung x not in lz hängt jedoch von der Liste ab, die erstellt wird. Wie kann dieser Code als Listenverständnis umgeschrieben werden?

2
kdkarthik 19 Feb. 2020 im 20:53

3 Antworten

Beste Antwort

Sie können dies in einem Listenverständnis nicht auf diese Weise tun, da Sie nicht mit der noch nicht vorhandenen Liste lz vergleichen können - vorausgesetzt, Sie versuchen, Duplikate in der resultierenden Liste wie in Ihrem Beispiel zu vermeiden.

Stattdessen können Sie den Python set verwenden Erzwingen Sie nur eine einzelne Instanz jedes Werts:

lz = set(x for x in lx if x in ly)

Und wenn Sie wirklich nach einer festgelegten Schnittmenge suchen (gemeinsame Elemente):

lz = set(lx) & set(ly)

UPDATE: Wie von @ Błotosmętek in den Kommentaren ausgeführt, wird bei Verwendung der Menge die Reihenfolge der Elemente nicht beibehalten, da die Menge per Definition ungeordnet ist. Wenn die Reihenfolge der Elemente signifikant ist, ist eine andere Strategie erforderlich.

3
Gavin H 19 Feb. 2020 im 18:13

Die richtige Antwort ist hier die Verwendung von Mengen, da (1) Mengen natürlich unterschiedliche Elemente haben und (2) Mengen effizienter sind als Listen für Mitgliedschaftstests. Die einfache Lösung ist also list(set(lx) & set(ly)).

Sets behalten jedoch nicht die Reihenfolge bei, in der Elemente eingefügt werden. Falls die Reihenfolge wichtig ist, finden Sie hier eine Lösung, bei der die Reihenfolge von lx beibehalten wird. (Wenn Sie die Reihenfolge von ly möchten, tauschen Sie einfach die Rollen der beiden Listen aus.)

def ordered_intersection(lx, ly):
    ly_set = set(ly)
    return [ly_set.remove(x) or x for x in lx if x in ly_set]

Beispiel:

>>> ordered_intersection(lx, ly)
[2, 4, 5]
>>> ordered_intersection(ly, lx)
[2, 5, 4]

Es funktioniert, weil ly_set.remove(x) immer None zurückgibt, was falsch ist, sodass ly_set.remove(x) or x immer den Wert x hat.

Der Grund, warum Sie dies mit einem einfacheren Listenverständnis wie lz = [... if x in lz] nicht tun können, ist, dass das gesamte Listenverständnis ausgewertet wird, bevor die resultierende Liste der Variablen lz zugewiesen wird. Der x in lz -Test ergibt also ein NameError, da es noch keine solche Variable gibt.


Das heißt, es ist möglich, Ihren Code so umzuschreiben, dass anstelle einer for - Schleife direkt ein Generatorausdruck (der einem Listenverständnis ähnelt) verwendet wird. aber es ist schlechter Code und Sie sollten dies nicht tun:

def ordered_intersection_bad_dont_do_this(lx, ly):
    lz = []
    lz.extend(x for x in lx if x in ly and x not in lz)
    return lz

Dies ist nicht nur schlecht, weil die Mitgliedschaft in Listen wiederholt getestet wird. es ist schlimmer, weil es von einem nicht spezifizierten Verhalten des {abhängt {X0}} Methode. Insbesondere wird jedes Element einzeln hinzugefügt, anstatt zuerst den Iterator zu erschöpfen und dann alle gleichzeitig hinzuzufügen. Die Dokumente sagen nicht, dass dies garantiert ist, daher funktioniert diese schlechte Lösung nicht unbedingt in anderen Versionen von Python.

0
kaya3 19 Feb. 2020 im 18:27

Wenn Sie set nicht verwenden möchten, kann dies ein anderer Ansatz sein, der das Listenverständnis verwendet.

lx = [1, 2, 3, 4, 5, 1, 2]
ly = [2, 5, 4]

lz=[]
[lz.append(x) for x in lx if (x in ly and x not in lz)]
print(lz)
0
pk786 19 Feb. 2020 im 19:08