Playing around with trees, I stumbled over this behaviour:
def descendants (self):
return #or "pass" or "42"
obviously returns None
.
On the other hand side:
def descendants (self):
return
yield 42
returns a generator which yields nothing (actually the behaviour I needed for leaf nodes).
Could somebody explain to me what is happening under the hood here?
Shouldn't the yield 42
be unreachable code? (I guess the decision whether a function is a generator or a "normal" function is made at compile time, based on whether it contains one or various yield
statements, be they reachable or not. But this is just a shot in the dark.)
The context is the following: I have trees and each node is either a tree or a leaf. Now I want to generate all the descendants of a node:
class Leaf (Node):
@property
def descendants (self):
return
yield 42
class Tree (Node):
@property
def descendants (self):
for child in self.children:
yield child
yield from child.descendants
You can use return once in a generator; it stops iteration without yielding anything, and thus provides an explicit alternative to letting the function run out of scope. So use yield to turn the function into a generator, but precede it with return to terminate the generator before yielding anything.
Python Generators are the functions that return the traversal object and used to create iterators. It traverses the entire items at once. The generator can also be an expression in which syntax is similar to the list comprehension in Python.
A Python generator is a function that produces a sequence of results. It works by maintaining its local state, so that the function can resume again exactly where it left off when called subsequent times. Thus, you can think of a generator as something like a powerful iterator.
yield in Python can be used like the return statement in a function. When done so, the function instead of returning the output, it returns a generator that can be iterated upon. You can then iterate through the generator to extract items. Iterating is done using a for loop or simply using the next() function.
As I understand it, the yield
keyword inside a function is detected at compile-time. The result is that the function no longer behaves like a normal function. When a function with a yield keyword is called, the function IMMEDIATELY returns a lazy generator object which produces variables as needed according to the defined function. The code in your function is only run when the generator is iterated through.
It's more succinctly explained here.
So descendants
is called, and since the yield
keyword is present in the function, a generator object is immediately returned. Since descendants
immediately return
s, however, the generator yields no values-but it's definitely still a generator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With