First of all, this post does NOT answer my question or give me any guide to answer my question at all.
My question is about mechanism function resolving non-local variables.
Code
# code block 1
def func():
    vals = [0, 0, 0]
    other_vals = [7, 8, 9]
    other = 12
    def func1():
        vals[1] += 1
        print(vals)
    def func2():
        vals[2] += 2
        print vals
    return (func1, func2)
f1, f2 = func()
Try to run f1, f2:
>>> f1()
[0, 1, 0]
>>> f2
[0, 1, 2]
This shows that the object previously referred by vals are shared by f1 and f2, and not garbage collected after execution of func.
Will objects referred by other_vals and other be garbage collected? I think so. But how does Python decide not to garbage collect vals?
Assumption 1
Python interpreter will resolve variable names within func1 and func2 to figure out references inside the function, and increase the reference count of [0, 0, 0] by 1 preventing it from garbage collection after the func call. 
But if I do
# code block 2
def outerfunc():
    def innerfunc():
        print(non_existent_variable)
f = outerfunc()
No error reported. Further more
# code block 3
def my_func():
    print(yet_to_define)
yet_to_define = "hello"
works.
Assumption 2 
Variable names are resolved dynamically at run time. This makes observations in code block 2 and 3 easy to explain, but how did the interpreter know it need to increase reference count of [0, 0, 0] in code block 1?
Which assumption is correct?
 
     
    