Make list changes to be local in Python?

Asked

Viewed 453 times

5

In Python as I do for the function below change the feather list locally, that is, only in the scope of the function f without changing x set outside of f?

x = [1, 2]
def f(x):
    x[1] = "novo valor"
    return x
f(x)

'''
f alterou o valor de x
'''

print x
[1, 'novo valor'] 

One solution I found was this::

def f(x):
    x = x[:]
    x[1] = "novo valor"
    return x

But that seemed a little forced. Is there some other way?

  • I’m having a little trouble understanding the root of your doubt. Is it because, in Python, objects are passed by reference? (i.e. it is the same object) While in C it can also be passed).

  • This, sometimes I want to make sure that the function will not have side effects, and I wanted to know the ways to do this in Python.

  • 1

    Just like most object-oriented imperative languages, there are not many resources to do this in Python, it ends up being the responsibility of the same programmer... That question on the Soen seems to corroborate that statement. You can even create a decorator who makes a deep copy of all the arguments of a function, but this does not guarantee against all cases (in particular cases like the function g of my answer).

2 answers

2


This is by design. A function accesses variables in its most general lexical scope (in this case, the top-level module) and can modify them at will. There is no copy being made. Even this is the reason why you should not use mutable data as parameters default function:

>>> def foo(x=[]):
...   x.append('bar')
...   print x
...
>>> foo()
['bar']
>>> foo()
['bar', 'bar']
>>> foo()
['bar', 'bar', 'bar']

If you need that within your function f there is a list identical to that defined outside at the time of the call, which you can modify at will without interfering with the original list, the only output is to make a copy even. And your way of copying a list is ok.

P.S. Rereading your question, I realize that this is independent of the function access a variable from outside or not:

x = [1, 2]
def f(y):
    y[1] = "novo valor"
    return y
f(x)

def g():
    x[1] = "novo valor"
    return x
g()

Since the parameter is mutable, the changes made in it will persist after the end of the function.


Updating: if you are looking for a generic way of copying arguments from functions to prevent them from being accidentally changed, one way is to create a decorator that makes a deep copy of all its arguments:

>>> from copy import deepcopy
>>> def sem_efeitos_colaterais(f):
...     def ret(*args, **kwargs):
...         args = [deepcopy(x) for x in args]
...         kwargs = { deepcopy(k):deepcopy(v) for k,v in kwargs.items() }
...         return f(*args, **kwargs)
...     return ret
...
>>> @sem_efeitos_colaterais
... def foo(x):
...     x.append(3)
...     return x
...
>>> x = [1,2]
>>> foo(x)
[1, 2, 3]
>>> x
[1, 2]

Note that this only ensures against changes in parameters, not against cases where the function accesses variables in its most general lexical scope (e.g.: the function g from the previous example). And, of course, it is good to stress that make copies of all has a negative impact on performance...

  • And there are other interesting/elegant ways to make this local copy of the argument besides the x[:]?

  • 1

    @Carloscinelli This one is the most "pythonic"... : P (interesting you have arrived at it alone, most people would use a solution least elegant - how x = list(x) for example)

  • Thanks, very good editing, this alternative can be interesting.

1

To make local changes to a list passed in an argument, you need to work on a copy of it.

x_local = list(x)
x_local[1] = 'Novo valor'

Browser other questions tagged

You are not signed in. Login or sign up in order to post.