list comprehension in exec with empty locals: NameError

前端 未结 5 1205
清歌不尽
清歌不尽 2020-12-15 22:55

Consider the following snippet:

def bar():
    return 1
print([bar() for _ in range(5)])

It gives an expected output [1, 1, 1, 1, 1]<

5条回答
  •  心在旅途
    2020-12-15 23:44

    Edit

    To answer your edited question, user @Hendrik Makait said bar is not in the scope of list comprehension:

    def foo():
        def bar():
            return 1
        print('bar' in globals()) # False, because the scope of foo and bar are diferents, foo is globals() scope, bar are in the scope of foo
        print('bar' in locals()) # True
        print(['bar' in locals() for _ in [1]]) # [False], because a new implicit scope is defined in list comprehension, as user @Antti Haapala said
        print([bar() for _ in [1, 2]]) # [1, 1]
    

    To answer the original question:

    If you create two different dictionaries, it wont recognize the local and globals definitions, the variables are not updated as @PM 2Ring said:

    exec("""
    def bar():
        return 1
    print(bar())
    print("bar" in globals())
    print("bar" in locals())
    print([bar() for _ in range(5)])
    """, {},{})
    

    it prints:

    1
    False #not in globals
    True
    Traceback (most recent call last):
      File "python", line 17, in 
      File "", line 7, in 
      File "", line 7, in 
    NameError: name 'bar' is not defined
    

    A way to do it, is update the variables, like this globals().update(locals()):

    exec("""
    def bar():
        return 1
    globals().update(locals())
    print("bar" in globals())
    print("bar" in locals())
    print([bar() for _ in range(5)])
    """, {}, {})
    

    wich gives:

    True
    True
    [1, 1, 1, 1, 1]
    

    But, if you remove the dictionaries, or create one and give it to the exec function as same parameter, it works:

    d={}
    
    exec("""
    def bar():
        return 1
    print("bar" in globals())
    print("bar" in locals())
    print([bar() for _ in range(5)])
    """,d,d)
    

    it prints:

    True
    True
    [1, 1, 1, 1, 1]
    

    That's why you get the error, it could't find your function in the globals

    Or simply, don't give the parameters:

    exec("""
    def bar():
        return 1
    print(bar())
    print("bar" in globals())
    print("bar" in locals())
    print([bar() for _ in range(5)])
    """)
    

    Cause the same efect.

提交回复
热议问题