1

I have two questions about this code:

def a_funcs():
    class A:
        def __init__(self):
            self.a = 0

    a_local = A()
    def set_a(a):
        a_local.a = a
    def get_a():
        return a_local.a
    return set_a, get_a

set_a, get_a = a_funcs()
print(get_a())
set_a(17)
print(get_a())

Does a_local keep existing after a_funcs have been returned? Is it still in the scope of a_funcs? Can it happen that the garbage collector deletes a_local?

Where is a_local stored technically? On the stack or on the heap?

habrewning
  • 735
  • 3
  • 12
  • Does this answer your second question? [CPython - Internally, what is stored on the stack and heap?](/q/2353552/4518341) TL;DR: It's an implementation detail, but in CPython, the heap. – wjandrea Jul 24 '22 at 17:18
  • Yes. For CPython that is the answer to my question. – habrewning Jul 24 '22 at 17:59
  • If it'd help your understanding, you could plug the code into [Python Tutor](https://pythontutor.com/visualize.html#mode=edit) and skip to the last step to see what references what. – wjandrea Jul 24 '22 at 18:16

2 Answers2

1

The name a_local is scoped in a_func. The variable is no longer aaccessible by this name after returning from the function. When you enter the function again, you will get a new one.

But the (mutable) value where it points to cannot be deleted by the garbage collector. There are still references to it, hidden in the function set_a and get_a. These functions are still accessible after returning from the function a_func.

The pointer to the piece of storage where the value of a_local is located is on the stack. But the storage for the value must be on heap because it keeps living after returning from a_func.

Donat
  • 4,157
  • 3
  • 11
  • 26
  • Can I then assume that everything in Python is stored on the heap and the stack only contains pointers to the actual data? Because in the moment when local_a is created, Python does not know yet that it is going to appear in a closure. – habrewning Jul 24 '22 at 17:57
  • @habrewning I am not quite sure how it has been implemented. In many cases the interpreter would be able to prove that a value cannot be accessed after returning from the function. It is a matter of optimization. – Donat Jul 24 '22 at 18:47
0

Not disagreeeing with @donat, but expanding upon what he wrote. Try:

set_a1, get_a1 = a_funcs()
set_a2, get_a2 = a_funcs()

set_a1(10)
set_a2(20)
print(get_a1())
print(get_a2())

You will see that each call to a_funcs() creates a new variable, and that the functions returned by a_funcs() refer to the variable that was just created.

Frank Yellin
  • 9,127
  • 1
  • 12
  • 22
  • Yes, that was clear. And I also tested this. But I was wondering how it can be that local variables keep existing any more. And that was answered. – habrewning Jul 24 '22 at 17:52