non vedo questo come un caso d'uso frequente, considerare questo come il generatore originale:
def original_gen():
for x in range(10):
should_break = yield x
if should_break:
break
Se il valore di should_break
viene sempre calcolato sulla base di una certa chiamata di funzione con x
allora perché non solo scrivere il generatore in questo modo:
def processing_gen(check_f):
for x in range(10):
yield x
should_break = check_f(x)
if should_break:
break
Tuttavia di solito penso al codice che elabora i valori generati come scritti all'interno del ciclo (altrimenti qual è il punto di avere un anello a tutti?)
Che sembra davvero si vuole fare è creare un generatore in cui si chiama il metodo __next__
davvero implica send(process(LAST_VALUE))
che può essere implementato con una classe:
class Followup_generator(): #feel free to use a better name
def __init__(self,generator,following_function):
self.gen = generator
self.process_f = following_function
def __iter__(self):
return self
def __next__(self):
if hasattr(self,"last_value"):
return self.send(self.process_f(self.last_value))
else:
self.last_value = next(self.gen)
return self.last_value
def send(self,arg):
self.last_value = self.gen.send(arg)
return self.last_value
def __getattr__(self,attr):
"forward other lookups to the generator (.throw etc.)"
return getattr(self.gen, attr)
# call signature is the exact same as @tobias_k's checking_generator
traversal = Followup_generator(bfs(g, start_node), process)
for n in traversal:
print(n)
n = traversal.send(DATA) #you'd be able to send extra values to it
Tuttavia, questo ancora non vede questo come di uso frequente, sarei perfettamente bene con un ciclo while
, anche se avevo messo la chiamata .send
in alto:
traversal = bfs(g, start_node)
send_value = None
while True:
n = traversal.send(send_value)
#code for loop, ending in calculating the next send_value
send_value = process(n)
E si potrebbe avvolgere che in un try: ... except StopIteration:pass
anche se trovo che semplicemente in attesa di un errore di sollevare è meglio espressa con un contesto manager:
class Catch:
def __init__(self,exc_type):
if issubclass(exc_type,BaseException):
self.catch_type = exc_type
else:
raise TypeError("can only catch Exceptions")
def __enter__(self):
return self
def __exit__(self,exc_type,err, tb):
if issubclass(exc_type, self.catch_type):
self.err = err
return True
with Catch(StopIteration):
traversal = bfs(g, start_node)
send_value = None
while True:
n = traversal.send(send_value)
#code for loop, ending in calculating the next send_value
send_value = process(n)
Beh, potresti renderlo molto più breve mettendo il try/tranne al di fuori del ciclo; questo ti farà risparmiare un set di try/except e if-condition. –
@tobias_k corretto, grazie. – max