Les réponses précédentes ne semblent pas satisfaire mes quatre tics obsessionnels compulsifs :
- Soyez aussi paresseux que possible,
- Évaluer l'Iterable original une seule fois
- Évaluer le prédicat une seule fois par élément
- Fournir des annotations de type agréables (pour python 3.7)
Ma solution n'est pas très jolie, et je ne pense pas pouvoir recommander son utilisation, mais la voici :
def iter_split_on_predicate(predicate: Callable[[T], bool], iterable: Iterable[T]) -> Tuple[Iterator[T], Iterator[T]]:
deque_predicate_true = deque()
deque_predicate_false = deque()
# define a generator function to consume the input iterable
# the Predicate is evaluated once per item, added to the appropriate deque, and the predicate result it yielded
def shared_generator(definitely_an_iterator):
for item in definitely_an_iterator:
print("Evaluate predicate.")
if predicate(item):
deque_predicate_true.appendleft(item)
yield True
else:
deque_predicate_false.appendleft(item)
yield False
# consume input iterable only once,
# converting to an iterator with the iter() function if necessary. Probably this conversion is unnecessary
shared_gen = shared_generator(
iterable if isinstance(iterable, collections.abc.Iterator) else iter(iterable)
)
# define a generator function for each predicate outcome and queue
def iter_for(predicate_value, hold_queue):
def consume_shared_generator_until_hold_queue_contains_something():
if not hold_queue:
try:
while next(shared_gen) != predicate_value:
pass
except:
pass
consume_shared_generator_until_hold_queue_contains_something()
while hold_queue:
print("Yield where predicate is "+str(predicate_value))
yield hold_queue.pop()
consume_shared_generator_until_hold_queue_contains_something()
# return a tuple of two generators
return iter_for(predicate_value=True, hold_queue=deque_predicate_true), iter_for(predicate_value=False, hold_queue=deque_predicate_false)
En testant ce qui suit, nous obtenons la sortie ci-dessous à partir des instructions d'impression :
t,f = iter_split_on_predicate(lambda item:item>=10,[1,2,3,10,11,12,4,5,6,13,14,15])
print(list(zip(t,f)))
# Evaluate predicate.
# Evaluate predicate.
# Evaluate predicate.
# Evaluate predicate.
# Yield where predicate is True
# Yield where predicate is False
# Evaluate predicate.
# Yield where predicate is True
# Yield where predicate is False
# Evaluate predicate.
# Yield where predicate is True
# Yield where predicate is False
# Evaluate predicate.
# Evaluate predicate.
# Evaluate predicate.
# Evaluate predicate.
# Yield where predicate is True
# Yield where predicate is False
# Evaluate predicate.
# Yield where predicate is True
# Yield where predicate is False
# Evaluate predicate.
# Yield where predicate is True
# Yield where predicate is False
# [(10, 1), (11, 2), (12, 3), (13, 4), (14, 5), (15, 6)]
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é.