Search code examples
pythonglobal-variables

Python "in-place" reassignment of a mutable object in the absence of setter methods


Let us have two functions:

def f1(x):
    x = do_stuff(x) # an instance of the same class as x
def f2(x):
    x.copy(do_stuff(x)) # ensures that x changes in-place to be identical to the argument of the method

The first one does not change the value of the global variable passed to it, since it just assigns the local name x to a new value. The second one does change it. Imagine a scenario, where the object passed to the function does not have a setter method. Is there a way to still change its global value?

P.S.: You can come up with workarounds like:

def f3(x):
    return do_stuff(x)
y = f3(y)

but what I am looking for is something like:

def f4(x):
    x inplace= do_stuff(x)
f1(y)

Each object has an ID that can be found using the id() function. Perhaps one could place a new value under that id? It looks like something that should be forbidden for immutable types, but is it possible for mutable types?


Clarifications

  • I am not looking for a way to change a specific global variable, but any global variable that happens to be passed to the function. This is why Python's global does not solve the problem.
  • Answers to this this question do not solve my problem. I know how variables are passed and I am not looking for an explanation. The answer closest to what I am asking was the one that suggests creating a wrapper that has a setter method. (Even a single membered list would do, since my_list[0] = new_value modifies the same list) Is there a solution without a wrapper? e.g. if you have an instance of a user-defined class, it is mutable. Maybe inside the function, you create another member of the class with different contents that you want to assign to the passed variable. Either your class has to have a "copy" method that takes another object of the same class and copies all the data. Or you have to go over the variables inside the function and copy them yourself. Is there a simpler or more general way of doing this?

Solution:

From the comments and answers below, it seems that the answer is no. In Python there is no general way that allows mutating any mutable object. The only way to mutate an object is via its methods or by directly accessing its variables. The solutions to this are either returning a new object that gets assigned to the global variable outside of the function, or creating a wrapper object.

Thanks to everyone helping me to figure this out and I hope this will help others, since I have not found this explicitly explained elsewhere.


Solution

  • Since variables in Python are not passed by reference. You're unable to do this for the general case.

    If you're inside a closure, i.e. function inside another function, you may use the nonlocal keyword.

    If you're changing a global, you may use the global keyword.

    If non of these conditions are met, you cannot reassign to the passed function without using real trickery such as ast (abstract syntax tree) or frame modifications using sys._getframe(). I don't suggest using any of these options as it leads to complicated code for no benefit whatsoever.

    Creating a new class which holds a single value might come handy:

    class Value:
        __slots__ = ("value",)
        def __init__(self, value):
            self.value = value
    
    x = Value(123)
    
    def test(x):
        x.value = 456