Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to convert this Python 2.7 code to Python 3?

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.

like image 279
wim Avatar asked Nov 06 '22 14:11

wim


1 Answers

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):
        ...
like image 102
user2357112 supports Monica Avatar answered Nov 14 '22 08:11

user2357112 supports Monica