Voici l'approche de l'itérateur paresseux :
from itertools import tee
def split_on_condition(seq, condition):
l1, l2 = tee((condition(item), item) for item in seq)
return (i for p, i in l1 if p), (i for p, i in l2 if not p)
Il évalue la condition une fois par élément et renvoie deux générateurs, le premier produisant des valeurs de la séquence lorsque la condition est vraie, l'autre lorsqu'elle est fausse.
Parce qu'il est paresseux, vous pouvez l'utiliser sur n'importe quel itérateur, même infini :
from itertools import count, islice
def is_prime(n):
return n > 1 and all(n % i for i in xrange(2, n))
primes, not_primes = split_on_condition(count(), is_prime)
print("First 10 primes", list(islice(primes, 10)))
print("First 10 non-primes", list(islice(not_primes, 10)))
En général, l'approche consistant à renvoyer une liste non fastidieuse est meilleure :
def split_on_condition(seq, condition):
a, b = [], []
for item in seq:
(a if condition(item) else b).append(item)
return a, b
Edit : Pour votre cas d'utilisation plus spécifique qui consiste à diviser les éléments en différentes listes en fonction d'une clé, voici une fonction générique qui fait cela :
DROP_VALUE = lambda _:_
def split_by_key(seq, resultmapping, keyfunc, default=DROP_VALUE):
"""Split a sequence into lists based on a key function.
seq - input sequence
resultmapping - a dictionary that maps from target lists to keys that go to that list
keyfunc - function to calculate the key of an input value
default - the target where items that don't have a corresponding key go, by default they are dropped
"""
result_lists = dict((key, []) for key in resultmapping)
appenders = dict((key, result_lists[target].append) for target, keys in resultmapping.items() for key in keys)
if default is not DROP_VALUE:
result_lists.setdefault(default, [])
default_action = result_lists[default].append
else:
default_action = DROP_VALUE
for item in seq:
appenders.get(keyfunc(item), default_action)(item)
return result_lists
Utilisation :
def file_extension(f):
return f[2].lower()
split_files = split_by_key(files, {'images': IMAGE_TYPES}, keyfunc=file_extension, default='anims')
print split_files['images']
print split_files['anims']
9 votes
J'ai atterri ici à la recherche d'un moyen d'avoir une condition dans l'instruction set builder, votre question a répondu à la mienne :)
8 votes
diviser est une description malheureuse de cette opération, puisqu'elle a déjà une signification spécifique en ce qui concerne les chaînes Python. Je pense que diviser est un terme plus précis (ou du moins moins surchargé dans le contexte des itérables Python) pour décrire cette opération. J'ai atterri ici à la recherche d'un équivalent de liste de
str.split()
, à diviser la liste en une collection ordonnée de sous-listes consécutives. Par exemplesplit([1,2,3,4,5,3,6], 3) -> ([1,2],[4,5],[6])
frente a diviser les éléments d'une liste par catégorie.0 votes
Discussion du même sujet sur python-list.
0 votes
IMAGE_TYPES devrait être un ensemble au lieu d'un tuple :
IMAGE_TYPES = set('.jpg','.jpeg','.gif','.bmp','.png')
. n(1) au lieu de n(o/2), avec pratiquement aucune différence de lisibilité.