Python-Liste Verständnis- "Pop" Ergebnis aus der Originalliste?

Sagen, ich habe eine Liste in Python 3.X. Ich benutze das Listenverständnis, um eine Untermenge dieser Liste zurückzugeben — gibt es eine einfache / Pythonische Art, "Pop" zu starten, die die ursprüngliche Liste unterteilt hat (also sind die Elemente in dieser Teilmenge nicht mehr in der ursprünglichen Liste, nachdem sie zurückgegeben wurden) ? Vielen Dank!

Beispiel (ich brauche Hilfe bei der Definition von my_func):

a = [1, 2, 2, 3, 4, 5] a, b = my_func(a, *kwargs) 

Ich möchte dann:

A = [1, 2, 2]
B = [3, 4, 5]

Anmerkung: Ich möchte nicht die Werte aus einer zu einer Zeit, sondern die ganze Teilmenge auf einmal. "Pop" kann nicht die beste Terminologie sein, aber ich weiß nicht was ist.

Der beste Weg, den ich mir vorstellen kann, ist:

  temp = [idx for idx, val in enumerate(a) if val > 2] b = [a[i] for i in temp] a = [val for idx,val in enumerate(a) if idx not in temp] 

Offensichtlich würde ich lieber etwas eleganter und effizienter. Ich möchte vermeiden, dass ich zweimal über die Liste gehe.

EDIT: Ich denke, diese Frage ist einzigartig, weil ich nicht einfach mit der Aufteilung der Liste beschäftigt bin – ich möchte die ursprüngliche Liste ändern. Das Aufteilen und Zuordnen eines dieser Splits zur ursprünglichen Listenvariable ist eine mögliche Lösung, aber ich hatte gehofft, dass es einen Weg gibt, es zu tun, ohne explizit die ursprüngliche Listenvariable zuzuordnen (ähnlich wie bei b.append (a.pop ( )))

4 Solutions collect form web for “Python-Liste Verständnis- "Pop" Ergebnis aus der Originalliste?”

Füllen Sie einfach die unerwünschten Gegenstände mit dem Listenverständnis aus:

 a = [1, 2, 2, 3, 4, 5] condition = lambda x: x > 2 b = [x for x in a if condition(x)] # b == [3, 4, 5] a = [x for x in a if not condition(x)] # a == [1, 2, 2] 

Aktualisieren

Wenn Sie sich Sorgen um Effizienz machen, dann ist hier ein anderer Ansatz, der die Liste nur einmal scannt:

 def classify(iterable, condition): """ Returns a list that matches the condition and a list that does not """ true_list = [] false_list = [] for item in iterable: if condition(item): true_list.append(item) else: false_list.append(item) return true_list, false_list a = [1, 2, 2, 3, 4, 5] b, a = classify(a, lambda x: x > 2) print(a) # [1, 2, 2] print(b) # [3, 4, 5] 

Update 2

Ich wusste nicht, dass mein Update sieht fast das gleiche wie das von user3467349, aber glaube es oder nicht, ich habe nicht betrügen 🙂

Denken Sie daran, das ist Python, nicht C, und manchmal ist die Art, wie es funktioniert, nicht unbedingt intuitiv. Dies bedeutet, dass Sie Ihre Annahmen überprüfen müssen. Ich habe das mit %%timeit Magic gemacht.

 a = [1,2,2,3,4,5] %timeit b=[x for x in a if x > 2]\ 1000000 loops, best of 3: 362 ns per loop %timeit c=[x for x in a if not (x > 2)] 1000000 loops, best of 3: 371 ns per loop 

Also nur unter 800 ns für beide, aber wir sind über die Schleife zweimal iterating. Sicherlich brauchen wir das nicht? Wie wäre es mit ein paar der oben genannten Methoden? Lass uns mit dem Klassifizieren beginnen, das ist ziemlich einfach und geht nur einmal über die Liste:

 %timeit b, a = classify(a, lambda x: x > 2) 1000000 loops, best of 3: 1.89 µs per loop 

Wow, obwohl es nur einmal über die Schleife geht, dauert es mehr als doppelt so lang wie die einfache Lösung, die zweimal läuft. Lass uns eine leichte Variation auf die andere oben vorgeschlagene Lösung ausprobieren:

 %%timeit b, c = [], [] for x in a: b.append(x) if x > 2 else a.append(x) 1000000 loops, best of 3: 1.2 µs per loop 

Besser, aber es ist immer noch langsamer als unsere "naive" / "ineffiziente" Umsetzung. Vielleicht wäre eine kleine Formulierung besser:

 %%timeit b, c = [], [] for x in a: if x > 2: b.append(x) else: c.append(x) 1000000 loops, best of 3: 983 ns per loop 

Hmm, das scheint fast das gleiche, aber ist ein wenig schneller. Immer noch nicht schlagen die naive Umsetzung. Lass uns richtig klug werden, vielleicht mach es noch schneller:

 %%timeit b, c = [], [] for x in a: (b, c)[x > 2].append(x) 1000000 loops, best of 3: 1.28 µs per loop 

Also, was wir sahen, ist, dass trotz unseres Wunsches, die Schleife nicht zweimal zu iterieren, scheinen wir nicht in der Lage zu sein, nur auf zwei Listenverständnisse zu verbessern. List Verständnisse sind Art von 'unter der Kapuze' in Python, und so für viele Dinge werden sie schneller als alles, was Sie kommen mit.

Nun ist der x < 2 Vergleich billig – keine Funktionsaufrufe, einfache Mathematik. Es wird ein Punkt geben, wo es anfängt, Sinn zu machen. Lassen Sie uns eine teurere Vergleichsfunktion schaffen – wir berechnen die Fakultät (und machen Sie es ineffizient):

 def factorial(x): if x in (0,1): return 1 else: return x * factorial(x-1) %timeit b = [x for x in a if factorial(x) > 6] 100000 loops, best of 3: 3.47 µs per loop %timeit c = [x for x in a if not factorial(x) > 6] 100000 loops, best of 3: 3.53 µs per loop 

Also, offensichtlich sind die Zeiten ein bisschen gegangen – jetzt sind bei etwa 7uS für alles.

Lass uns mal unsere anderen Beispiele ausprobieren:

 %timeit b, c = classify(a, lambda x: factorial(x) > 6) 100000 loops, best of 3: 5.05 µs per loop %%timeit b, c = [], [] for x in a: if factorial(x) > 6: b.append(x) else: c.append(x) 100000 loops, best of 3: 4.01 µs per loop %%timeit b, c = [], [] for x in a: (b, c)[factorial(x) > 6].append(x) 100000 loops, best of 3: 4.59 µs per loop 

Die Lektion von all dem: Im Umgang mit Python & Effizienz, ist es in der Regel eine gute Idee, nur probieren Sie es in einer Konsole, aber sehr oft die naiven Implementierungen sind vernünftig performant & am einfachsten zu lesen. Und ein schneller Test wird Ihnen sagen, wenn der Versuch zu optimieren ist es wirklich wert, Sie können es oft weniger lesbar und langsamer machen, wenn Sie nicht vorsichtig sind ….

Addendum: Jemand kommentiert, dass wir längere Listen brauchen, denn wir messen die Funktion Aufruf über Kopf mehr als Leistung. Sie haben einen guten Punkt, aber die Zeiten zeigen über die gleiche Beziehung auf meiner Maschine:

 In [16]: a = range(100000) In [17]: random.shuffle(a) In [18]: %timeit b = [x for x in a if x > 50000] 100 loops, best of 3: 5.2 ms per loop In [19]: %timeit c = [x for x in m if not x > 50000] 100 loops, best of 3: 5.18 ms per loop In [20]: %timeit c = [x for x in m if x <= 50000] 100 loops, best of 3: 5.35 ms per loop In [21]: %%timeit ....: b, c = [], [] ....: for x in a: ....: if x > 50000: ....: b.append(x) ....: else: ....: c.append(x) ....: 100 loops, best of 3: 12.7 ms per loop 

Beachten Sie, dass, wenn ich den Vergleich zu x> 2 (anstelle von x> 50000) ändern die zweite Schleife bis zu etwa 11,4ms beschleunigt. Aber trotzdem ist das kaum noch schneller als die naive Umsetzung. Das heißt, es ist wahrscheinlich das, was ich bevorzugen würde – es ist immer noch leicht zu lesen, und es ist nicht langsamer (oder deutlich so). Und Code neigt dazu, komplexer im Laufe der Zeit zu bekommen, also wenn Sie später einen weiteren Vergleich hinzufügen oder ändern Sie es zu einem Funktionsaufruf, ist es einfacher zu tun, und die Leistung wird weniger leiden in dieser Version als die naive Version.

Aber wieder: Das ist nicht zu sagen "Gebrauchslistenverständnis" (obwohl sie sehr oft eine gute Idee sind), aber überprüfen Sie Ihre Annahmen.

Die Einleitungslösung (bitte nicht wirklich verwenden):

 a = [7,4,2, 1, 5 , 11, 2] [x for x in (a.pop() for i in range(len(a))) if (lambda x: True if x> 2 \ else a.insert(0, x))(x)] 

Mehr vernünftige Lösungen

Im Allgemeinen – wenn Sie überprüfen, um zu sehen, was ein Wert ist vor knallend – das ist ein Lesen in einem Index, und dann ein Pop (nicht zu erwähnen, die funkiness der Verfolgung der Iteration über eine Reihe von sich ändernden Länge), die zu sein scheint Mach den Punkt, dass du keine neuen Listen hast.

Also würde ich einfach zu einer von zwei neuen Listen hinzufügen

 a = [1, 2, 2, 3, 4, 5] b=[]; c=[]; for i in range(len(a)): if x > 2: b.append(x) else: c.append(x) 

Natürlich kannst du technisch Pop aus einem und einfügen zurück in eine Sie würde a.insert(0, x) anstelle von c.append(x) – aber es ist in der Regel eine schlechte Idee, eine Liste zu manipulieren, die Sie c.append(x) sind.

Eine andere Alternative ist eine sortierte Liste und ein bisect ie

 a = sorted(a) ind = bisect.bisect(a, 2) #Now both lists are here a, b= a[:ind], a[ind:] 

Welche Methode ist vorzuziehen, hängt wirklich davon ab, was Sie mit den Listen danach machen wollen.

Wenn du die Werte aus der Liste a 'pop' 'anfügen willst und diese Werte an b anhabe, würde ich diesem Ansatz folgen:

  a = [1,2,2,3,4,5] b = [] for i in a[:]: if i > 2: b.append(i) a.remove(i) #a = [1,2,2] #b = [3,4,5] 

Dieses Skript ist besonders hilfreich, wenn die Liste a nicht in einer bestimmten Reihenfolge war.

Python ist die beste Programmiersprache der Welt.