0

I'm trying to learn more about generator functions in Python. To my knowledge, generator functions don't fully return until there are no more yield calls, so a stack frame exists in the generator returned by the function.

Stack frames should have references to a callable function, so my question is: how can I get that callable function from the generator?

When running the code below, I have a generator function, test().

def test():
    for i in range(10):
        yield i

generator = test()

In this example, is there any way to get the callable function test() from generator?

After looking at this answer, it seems like CPython keeps track of some of these like generator.frame and generator.code, however, I can't seem to convert those objects into functions.

I need the callable function. Something like this:

func = generator.something_special
new_generator = func()
Michael M.
  • 10,486
  • 9
  • 18
  • 34
  • 2
    Your code never tried to print the stack from within the `test()` function so it's never going to be in the current stack - it literally `yield`ed the execution back to its caller (i.e. `main()`). If you want a bit more detail [this thread](https://stackoverflow.com/questions/25232350/how-generators-work-in-python) dives into how generators work. – metatoaster Oct 20 '22 at 02:50
  • "Where are the stack frames" - in memory, but not in the current stack. They are only in the stack while they are active. – kaya3 Oct 20 '22 at 03:40
  • @kaya3 Ok, I see. Is there any way to get the names of the functions that are in this memory but not active? – Michael M. Oct 20 '22 at 03:42
  • Not unless you have some general way of getting all of the objects which exist in memory, even those which you don't have access to via references. What is the reason that you want access to inactive stack frames? – kaya3 Oct 20 '22 at 03:44
  • @kaya3 I've tried to update my question to better explain what I'm trying to say. What I really want to do is get a function name from a generator after it has been returned. – Michael M. Oct 20 '22 at 03:50

1 Answers1

1

You can use the __name__ attribute to get the name of the original function then access it inside locals provided it's still in scope.

def test():
    for i in range(10):
        yield i


generator = test()
print(list(generator)) 
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]


func_name = generator.__name__ 
new_generator = locals()[func_name]()
print(func_name, list(new_generator)) 
# test [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
0x263A
  • 1,807
  • 9
  • 22
  • I found that in some cases (ie: when inside a function), you need to use `globals()` instead of `locals()`. However, using `globals()` will make it always work regardless of scope. – Michael M. Oct 20 '22 at 04:37
  • 1
    @MichaelM.Yeah if the function is outside of the scope `locals()` won't pick it up but `globals()` usually will. Heads up btw using globals and locals gets risky fast. I recommend doing some googling on some of the pitfalls of using them. – 0x263A Oct 20 '22 at 04:37