Hyperboreus
Hyperboreus

Reputation: 32429

Empty generator

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

Upvotes: 5

Views: 793

Answers (1)

jayelm
jayelm

Reputation: 7657

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 returns, however, the generator yields no values-but it's definitely still a generator.

Upvotes: 5

Related Questions