How does exec work with locals?

前端 未结 3 783
小鲜肉
小鲜肉 2020-11-22 07:13

I thought this would print 3, but it prints 1:

def f():
    a = 1
    exec(\"a = 3\")
    print(a)
3条回答
  •  猫巷女王i
    2020-11-22 07:57

    The reason that you can't change local variables within a function using exec in that way, and why exec acts the way it does, can be summarized as following:

    1. exec is a function that shares its local scope with the scope of the most inner scope in which it's called.
    2. Whenever you define a new object within a function's scope it'll be accessible in its local namespace, i.e. it will modify the local() dictionary. When you define a new object in exec what it does is roughly equivalent to following:

    from copy import copy
    class exec_type:
        def __init__(self, *args, **kwargs):
            # default initializations
            # ...
            self.temp = copy(locals())
    
        def __setitem__(self, key, value):
            if var not in locals():
                set_local(key, value)
            self.temp[key] = value
    

    temp is a temporary namespace that resets after each instantiation (each time you call the exec).


    1. Python starts looking up for the names from local namespace. It's known as LEGB manner. Python starts from Local namespce then looks into the Enclosing scopes, then Global and at the end it looks up the names within Buit-in namespace.

    A more comprehensive example would be something like following:

    g_var = 5
    
    def test():
        l_var = 10
        print(locals())
        exec("print(locals())")
        exec("g_var = 222")
        exec("l_var = 111")
        exec("print(locals())")
    
        exec("l_var = 111; print(locals())")
    
        exec("print(locals())")
        print(locals())
        def inner():
            exec("print(locals())")
            exec("inner_var = 100")
            exec("print(locals())")
            exec("print([i for i in globals() if '__' not in i])")
    
        print("Inner function: ")
        inner()
        print("-------" * 3)
        return (g_var, l_var)
    
    print(test())
    exec("print(g_var)")
    

    Output:

    {'l_var': 10}
    {'l_var': 10}
    

    locals are the same.

    {'l_var': 10, 'g_var': 222}
    

    after adding g_var and changing the l_var it only adds g_var and left the l_var unchanged.

    {'l_var': 111, 'g_var': 222}
    

    l_var is changed because we are changing and printing the locals in one instantiation ( one call to exec).

    {'l_var': 10, 'g_var': 222}
    {'l_var': 10, 'g_var': 222}
    

    In both function's locals and exec's local l_var is unchanged and g_var is added.

    Inner function: 
    {}
    {'inner_var': 100}
    {'inner_var': 100}
    

    inner_function's local is same as exec's local.

    ['g_var', 'test']
    

    global is only contain g_var and function name (after excluding the special methods).

    ---------------------
    
    (5, 10)
    5
    

提交回复
热议问题