How to convert this Python 2.7 code to Python 3?

浪尽此生 提交于 2021-02-16 16:32:13

问题


The following code works in Python 2.7, to dynamically inject local variables into a function scope:

myvars = {"var": 123}

def func():
    exec("")
    locals().update(myvars)
    print(var)

func()
# assert "var" not in globals()

It's a bit subtle, but the presence of an exec statement indicates to the compiler that the local namespace may be modified. In the reference implementation, it will transform the lookup of the name "locals" from a LOAD_GLOBAL op into a LOAD_NAME op, enabling addition to the local namespace.

In Python 3, exec became a function instead of a statement, and the locals() call returns a copy of the namespace, in which modifications have no effect.

How can you recreate the idea in Python 3, to dynamically create local variables inside a function? Or is this a "feature" only possible in Python 2.x?

Note: The code must not bind the names in outer scopes.


回答1:


I wouldn't recommend it, but you could put the body of the function in a class statement:

myvars = {"var": 123}

def func():
    class Bleh:
        locals().update(myvars)
        print(var)

func()

As with the Python 2 code, the fact that modifying locals() works in this case is technically undocumented and subject to change. A class scope must use an actual dict for local variable resolution, but theoretically, a later Python implementation might copy that dict for locals() or something like that.

As with the Python 2 code, doing this will interfere with closure variable resolution, including subtle cases like the following:

myvars = {"var": 123}

def func():
    class Bleh:
        locals().update(myvars)
        print([var for x in (1, 2, 3)])

func()

or even

def func():
    class Bleh:
        var = 123
        print([var for x in (1, 2, 3)])

func()

Both of these cases result in a NameError, because the list comprehension creates a new scope that doesn't have access to var.

In Python 2, trying to use closure variables with exec would lead to a SyntaxError. If you try to replicate the functionality in Python 3 with a class statement, you don't get any such detection.

Finally, Python will try to build a class after the class statement is done, which could cause weird issues with things like __set_name__ methods running unexpectedly. You could fix this issue, at least, by using a metaclass:

class NoClass(type):
    def __new__(self, *args, **kwargs):
        return None

def func():
    class Bleh(metaclass=NoClass):
        ...


来源:https://stackoverflow.com/questions/57595351/how-to-convert-this-python-2-7-code-to-python-3

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!