397 Stimmen

Wie kann man eine Liste anhand einer Bedingung aufteilen?

Was ist der beste Weg, sowohl ästhetisch als auch von der Leistung her, um eine Liste von Elementen in mehrere Listen auf der Grundlage einer Bedingung aufzuteilen? Das Äquivalent von:

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

Gibt es eine elegantere Möglichkeit, dies zu tun?

Hier ist der eigentliche Anwendungsfall, um besser zu erklären, was ich zu tun versuche:

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

0voto

Jim Witschey Punkte 285

Ich würde einen 2-Pass-Ansatz wählen und die Auswertung des Prädikats vom Filtern der Liste trennen:

def partition(pred, iterable):
    xs = list(zip(map(pred, iterable), iterable))
    return [x[1] for x in xs if x[0]], [x[1] for x in xs if not x[0]]

Das Schöne daran ist, dass die Leistung (neben der Bewertung der pred nur einmal für jedes Mitglied von iterable ), ist, dass ein großer Teil der Logik aus dem Interpreter in einen hochoptimierten Iterations- und Mapping-Code verlagert wird. Dies kann die Iteration über lange Iterablen beschleunigen, wie beschrieben in dieser Antwort .

In Bezug auf die Ausdruckskraft nutzt es ausdrucksstarke Idiome wie Comprehensions und Mapping.

0voto

Shreyas Punkte 1124

Hier gibt es bereits einige Lösungen, aber eine weitere Möglichkeit wäre, dass -

anims = []
images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]

Iteriert nur einmal über die Liste und sieht für mich ein wenig pythonischer und damit lesbarer aus.

>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file1.bmp', 33L, '.bmp')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> anims = []
>>> images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]
>>> print '\n'.join([str(anims), str(images)])
[('file2.avi', 999L, '.avi')]
[('file1.jpg', 33L, '.jpg'), ('file1.bmp', 33L, '.bmp')]
>>>

0voto

emvee Punkte 4221
def partition(pred, seq):
  return reduce( lambda (yes, no), x: (yes+[x], no) if pred(x) else (yes, no+[x]), seq, ([], []) )

0voto

Chrisjan Punkte 163
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f not in images]

Schön, wenn die Bedingung länger ist, wie in Ihrem Beispiel. Der Leser muss nicht herausfinden, wie die negative Bedingung lautet und ob sie alle anderen Fälle abdeckt.

0voto

Shikhar Mall Punkte 151

Manchmal braucht man die andere Hälfte der Liste nicht. Zum Beispiel:

import sys
from itertools import ifilter

trustedPeople = sys.argv[1].split(',')
newName = sys.argv[2]

myFriends = ifilter(lambda x: x.startswith('Shi'), trustedPeople)

print '%s is %smy friend.' % (newName, newName not in myFriends 'not ' or '')

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