TLDR: You are overwriting the variable name, so the interpreter will simply use the latest definition. Once you run saying = a.f1(saying)
, there is no longer any way to directly call your original saying
unless you have saved it in another variable.
If you want a longer explanation...
Garbage Collection
Normally when you overwrite variables like this, the old value / place in memory is completely erased by the garbage collector. However, in your case the original saying
is saved in memory since it is used when calling f1(saying)
. You just can't access it, again unless you have saved it in another variable before reassigning it.
Let's look at the memory references using the id
function to get a better idea of what's going on.
If you run this code:
def f1(input_func):
def inner_func():
input_func()
input_func()
return inner_func
def saying():
print('Hello')
print("saying address:", hex(id(saying)))
saying()
print("---")
saying = f1(saying)
print("new saying address = f1(saying):", hex(id(saying)))
saying()
You'll get something like this:
saying address: 0x7fa960295820
Hello
---
new saying address = f1(saying): 0x7fa9602958b0
Hello
Hello
You can see that the two functions are saved in different memory addresses, and saying
now points to the new address.
If we save the first address ahead of time, we can use this technique to recover the original function. You would never want to do this in practice (it is infinitely easier to simply save the original saying
in a different variable), but for instructional purposes only:
import gc
# ... define f1 and saying as before
def object_by_id(id_):
for obj in gc.get_objects():
if id(obj) == id_:
return obj
raise Exception("Not found")
origId = id(saying)
print("saying address:", hex(origId))
saying()
print("---")
saying = f1(saying)
print("new saying address = f1(saying):", hex(id(saying)))
saying()
print("---")
saying = object_by_id(origId)
print("saying address after recovery: ", hex(id(saying)))
if id(saying) == origId:
print( "Matches original!")
saying()
Output:
saying address: 0x7f0c2db4a820
Hello
---
new saying address = f1(saying): 0x7f0c2db4a8b0
Hello
Hello
---
saying address after recovery: 0x7f0c2db4a820
Matches original!
Hello
So you can see that the original function has indeed been recovered. Again, this only works because the new value of saying = f1(saying)
contains in its definition a call to the original function.
However, what happens if the call to f1
doesn't include saying
, but instead calls another function? Then the original saying
is lost permanently. You can see this by adding this snippet to the end of the previous code:
def otherFunc():
print(1)
saying = f1(otherFunc)
print("new saying address = f1(otherFunc):", hex(id(saying)))
saying()
print("---")
saying = object_by_id(origId) # Exception: Not found
No variables reference the original saying
any longer, so the garbage collector removes it and it is no longer recoverable at all.
Demo