Wat is het nut van het yield
sleutelwoord in Python? Wat doet het?
Bijvoorbeeld, ik'probeer deze code te begrijpen1:
def _get_child_candidates(self, distance, min_dist, max_dist):
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
En dit is de beller:
result, candidates = [], [self]
while candidates:
node = candidates.pop()
distance = node._get_dist(obj)
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
Wat gebeurt er als de methode _get_child_candidates
wordt aangeroepen?
Wordt er een lijst geretourneerd? Een enkel element? Wordt de methode opnieuw aangeroepen? Wanneer stoppen de volgende aanroepen?
Zie het zo:
Een iterator is gewoon een mooie klinkende term voor een object dat een next()
methode heeft. Dus een iterator functie eindigt in iets als dit:
Originele versie:
def some_function():
for i in xrange(4):
yield i
for i in some_function():
print i
Dit is in principe wat de Python interpreter doet met de bovenstaande code:
class it:
def __init__(self):
# Start at -1 so that we get 0 when we add 1 below.
self.count = -1
# The __iter__ method will be called once by the 'for' loop.
# The rest of the magic happens on the object returned by this method.
# In this case it is the object itself.
def __iter__(self):
return self
# The next method will be called repeatedly by the 'for' loop
# until it raises StopIteration.
def next(self):
self.count += 1
if self.count < 4:
return self.count
else:
# A StopIteration exception is raised
# to signal that the iterator is done.
# This is caught implicitly by the 'for' loop.
raise StopIteration
def some_func():
return it()
for i in some_func():
print i
Voor meer inzicht in wat'er achter de schermen gebeurt, kan de for
lus herschreven worden tot dit:
iterator = some_func()
try:
while 1:
print iterator.next()
except StopIteration:
pass
Is dat logischer of verwar je alleen maar meer :)
Ik moet opmerken dat dit een oversimplificatie is voor illustratieve doeleinden :)
yield
is net als return
- het geeft terug wat je zegt dat het moet teruggeven (als een generator). Het verschil is dat de volgende keer dat je de generator aanroept, de uitvoering start vanaf de laatste aanroep van het yield
statement. In tegenstelling tot return, wordt het stack frame niet opgeschoond wanneer een yield optreedt, maar de controle wordt teruggegeven aan de aanroeper, dus de status wordt hervat wanneer de functie de volgende keer wordt aangeroepen.
In het geval van jouw code, gedraagt de functie get_child_candidates
zich als een iterator, zodat wanneer je de lijst uitbreidt, er één element per keer aan de nieuwe lijst wordt toegevoegd.
list.extend
roept een iterator aan totdat deze is uitgeput. In het geval van het codevoorbeeld dat je postte, zou het veel duidelijker zijn om gewoon een tupel terug te geven en die aan de lijst toe te voegen.
Het geeft een generator terug. Ik'ben niet bijzonder vertrouwd met Python, maar ik geloof dat het'hetzelfde soort ding is als C#'s iterator blocks als je daarmee vertrouwd bent.
Het kernidee is dat de compiler/interpreter/whatever wat trucjes uithaalt zodat wat de aanroeper betreft, ze next() kunnen blijven aanroepen en het zal waarden blijven teruggeven - alsof de generator methode gepauzeerd was. Nu kun je'tuurlijk niet echt "pauzeren" een methode, dus de compiler bouwt een toestandsmachine voor je om te onthouden waar je nu bent en hoe de lokale variabelen enz. eruit zien. Dit is veel gemakkelijker dan zelf een iterator te schrijven.