Python variable reference return

Asked

Viewed 1,564 times

3

When we define a variable, basically we are naming a memory address, follows a script that represents my doubt:

class Spam:

    _value = []

    @classmethod
    def value(cls):
        return cls._value

>>> Spam.value()
[]

So far so good but the problem is that the class method, returns a reference to a variable defined so far as "privada", now I can do:

>>> var = Spam.value()
>>> var.append(5)
>>> Spam.value()
[5]
>>> var is Spam.value()
True

In some codes I am reading they avoid returning a reference to their own variable, and return a copy of it as follows:

class Spam:

    _value = []

    @classmethod
    def value(cls):
        return cls._value.copy()

>>> Spam.value()
[]
>>> var = Spam.value()
>>> var.append(5)
>>> var
[5]
>>> Spam.value()
>>> []
>>> var is Spam.value()
False

Is there any other way to avoid returning the Python reference?

3 answers

2

When we define a variable, basically we are naming a memory address

This is incorrect, for other languages, but not for python.

In python, there are no variables. What exists are names; The best analogy I’ve found are paper labels with a name on it.

In python, there are no different values. Only exists a single type of value, that is "reference". In python, variables (names) do not represent memory addresses as in some other languages. The names keep only references, who allocates and uses memory are the objects themselves and not the names.

In short, actually what we have are names and references. Each name holds a value that is a reference to an object.

When an object no longer has any reference to it, it is removed from memory and the space it occupied is automatically released.

For example:

x = 5

After executing the above line, x is declared as a name that has a reference to the object of type int 5

y = x

Now, y is another name that has a reference to same object that x. At this point we have only one object in memory, the 5; We have two names x and y both have references to the same object 5.

x = x + 1

Now we are performing a sum operation, ie, int.__add__(x, 1) that calf and returns a new object of the type int, the 6. From that point on x has a reference to this new object int. The name y continues to refer to the object 5. We therefore have two names and two objects in memory.

The situation complicates when using mutable objects as lists:

a = []
b = a
a.append('Teste')

In the example above, there is only one list, created in the first line of that piece of code. The method append() does not create a new list, he modifies the existing list you refer to, so you still have two names a and b referring to the same object.

def func(x):
    x = 2

a = []
func(a)

If the passage were by reference, x = 2 would alter a; however, the passage is by value, and the value is a reference. x = 2 simply makes the local name x refers to another object, the 2; The name a continues to refer to the list []. It is impossible to change the value of a (which is a reference) through the parameter x, because that reference has been passed by value into the function func.

You can use the reference you passed to modify the object itself, if it is mutable, but you cannot use it to modify who the name you passed refers to.

In python, all parameter passages and all returns are for value however this value is always a reference! It is not the same as passing by reference, as it is impossible, from within the function, to change the reference of the name that was passed as parameter.

Is there any other way to avoid returning the Python reference?

In his first example, the return is returning the value associated with the name _value, but this value is a reference to the list previously created:

return cls._value

That is, this way of functioning is part of the language and there is no way to modify it. All passages are by value and all values will always be references.

0

If you write:

a = []
b = a
a.append(5)
print(a)
print(b)

'a' will always be equal to b. no matter what changes are made.

To fix this, you can use the copy method():

a = []
b = a.copy()
a.append(5)
print(a)
print(b)

This way, another list will be created and 'a' and 'b' will be different objects.

0

What we want is basically to make list_2 = list_1 without list_2 being a reference to list_1, that is, we want to copy only the values.

Another solution besides copy() is to use deepcopy function():

from copy import deepcopy

lista_1 = [1,4]
lista_2 = deepcopy(lista_1)
lista_2.append('mudei aqui')

>>> print('lista_1:', lista_1)
lista_1: [1, 4]
>>> print('lista_2:', lista_2)
lista_2: [1, 4, 'mudei aqui']

Another way to copy only values from a list is:

lista_1 = []
lista_2 = lista_1[:]
lista_2.append('mudei aqui')

>>> print('lista_1:', lista_1)
lista_1: []
>>> print('lista_2:', lista_2)
lista_2: ['mudei aqui']

Using your class, you’d be:

class Spam:

    _value = []

    @classmethod
    def value(cls):
        return cls._value[:]
        #OU return deepcopy(cls._value)

Basically the answer boils down to: copy the values (value by value) and not the object. We can even make a function for this (since the method lista_1[:] does not work for matrices), but copy() and deepcopy() already solve...

def copia_lista(lista):
    lista_copia = []
    for i in range(0, len(lista)):
        lista_copia.append(lista[i])
    return lista_copia

def copia_matriz(matriz):
    matriz_copia = []
    for i in range(0, len(matriz)):
        linha_copia = []
        for j in range(0, len(matriz[i])):
            linha_copia.append(matriz[i][j])
        matriz_copia.append(linha_copia)
    return matriz_copia

class Spam:

    _value = []

    @classmethod
    def value(cls):
        return copia_lista(cls._value)
  • Your answer is wrong. In the example that says "But be careful when using this function" the code does not behave the way you put it in the answer. The copy is enough in that case, it is not necessary to use deepcopy as there are no lists within lists.

  • @nosklo, really, I made a mistake with the prints and ended up talking nonsense. I edited the answer.

Browser other questions tagged

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