Objetos mutables vs inmutables en Python: una guía visual y práctica

Python es un lenguaje asombroso. Debido a su simplicidad, muchas personas lo eligen como su primer lenguaje de programación.

Los programadores experimentados también usan Python todo el tiempo, gracias a su amplia comunidad, abundancia de paquetes y sintaxis clara.

Pero hay un problema que parece confundir tanto a los principiantes como a algunos desarrolladores experimentados: los objetos de Python. Específicamente, la diferencia entre objetos mutables e inmutables .

En esta publicación profundizaremos nuestro conocimiento de los objetos de Python, aprenderemos la diferencia entre objetos mutables e inmutables y veremos cómo podemos usar el intérprete para comprender mejor cómo funciona Python.

Usaremos funciones y palabras clave importantes como idy is, y entenderemos la diferencia entre x == yy x is y.

¿Estás preparado para ello? Empecemos.

En Python, todo es un objeto

A diferencia de otros lenguajes de programación en los que el lenguaje admite objetos, en Python realmente todo es un objeto, incluidos enteros, listas e incluso funciones.

Podemos utilizar nuestro intérprete para verificar que:

>>> isinstance(1, object) True >>> isinstance(False, object) True def my_func(): return "hello" >>> isinstance(my_func, object) True

Python tiene una función incorporada, idque devuelve la dirección de un objeto en la memoria. Por ejemplo:

>>> x = 1 >>> id(x) 1470416816

Arriba, creamos un objeto con el nombre de xy le asignamos el valor de 1. Luego usamos id(x)y descubrimos que este objeto se encuentra en la dirección 1470416816en la memoria.

Esto nos permite comprobar cosas interesantes sobre Python. Digamos que creamos dos variables en Python, una con el nombre de xy otra con el nombre de y, y les asignamos el mismo valor. Por ejemplo, aquí:

>>> x = "I love Python!" >>> y = "I love Python!"

Podemos usar el operador de igualdad ( ==) para verificar que efectivamente tengan el mismo valor a los ojos de Python:

>>> x == y True

¿Pero son estos el mismo objeto en la memoria? En teoría, aquí puede haber dos escenarios muy diferentes.

Según el escenario (1) , en realidad tenemos dos objetos diferentes, uno con el nombre de xy otro con el nombre de y, que resulta que tienen el mismo valor.

Sin embargo, también podría darse el caso de que Python realmente almacene aquí solo un objeto, que tiene dos nombres que hacen referencia a él, como se muestra en el escenario (2) :

Podemos usar la idfunción presentada anteriormente para verificar esto:

>>> x = "I love Python!" >>> y = "I love Python!" >>> x == y True >>> id(x) 52889984 >>> id(y) 52889384

Como podemos ver, el comportamiento de Python coincide con el escenario (1) descrito anteriormente. Aunque x == yen este ejemplo (es decir, xy ytengan los mismos valores ), son objetos diferentes en la memoria. Esto se debe a que id(x) != id(y), como podemos verificar explícitamente:

>>> id(x) == id(y) False

Hay una forma más corta de hacer la comparación anterior, y es usar el isoperador de Python . Verificar si x is yes lo mismo que verificar id(x) == id(y), lo que significa si xy yson el mismo objeto en la memoria:

>>> x == y True >>> id(x) == id(y) False >>> x is y False

Esto arroja luz sobre la importante diferencia entre el operador de igualdad ==y el operador de identidad is.

Como se puede ver en el ejemplo anterior, es completamente posible que los dos nombres en Python ( xy y) estar sujetos a dos objetos diferentes (y por tanto, x is yes False), donde estos dos objetos tienen el mismo valor (por lo que x == yes True).

¿Cómo podemos crear otra variable que apunte al mismo objeto al que xapunta? Simplemente podemos usar el operador de asignación =, así:

>>> x = "I love Python!" >>> z = x

Para verificar que efectivamente apuntan al mismo objeto, podemos usar el isoperador:

>>> x is z True

Por supuesto, esto significa que tienen la misma dirección en la memoria, como podemos verificar explícitamente usando id:

>>> id(x) 54221824 >>> id(z) 54221824

Y, por supuesto, tienen el mismo valor, por lo que también esperamos x == zvolver True:

>>> x == z True

Objetos mutables e inmutables en Python

Hemos dicho que todo en Python es un objeto, sin embargo, existe una distinción importante entre objetos. Algunos objetos son mutables mientras que otros son inmutables .

Como mencioné antes, este hecho causa confusión a muchas personas que son nuevas en Python, así que nos aseguraremos de que sea claro.

Objetos inmutables en Python

Para algunos tipos en Python, una vez que hemos creado instancias de esos tipos, nunca cambian. Son inmutables .

Por ejemplo, los intobjetos son inmutables en Python. ¿Qué pasará si intentamos cambiar el valor de un intobjeto?

>>> x = 24601 >>> x 24601 >>> x = 24602 >>> x 24602

Bueno, parece que cambiamos con xéxito. Aquí es exactamente donde mucha gente se confunde. ¿Qué pasó exactamente aquí debajo del capó? Usemos idpara investigar más a fondo:

>>> x = 24601 >>> x 24601 >>> id(x) 1470416816 >>> x = 24602 >>> x 24602 >>> id(x) 1470416832

Entonces podemos ver que al asignar x = 24602, no cambiamos el valor del objeto al que se xhabía vinculado antes. Más bien, creamos un nuevo objeto y le vinculamos el nombre x.

Así que después de asignar 24601a xmediante el uso x = 24601, tuvimos el siguiente estado:

Y después de usar x = 24602, creamos un nuevo objeto y vinculamos el nombre xa este nuevo objeto. El otro objeto con el valor de 24601ya no es accesible por x(o cualquier otro nombre en este caso):

Whenever we assign a new value to a name (in the above example - x) that is bound to an int object, we actually change the binding of that name to another object.

The same applies for tuples, strings (str objects), and bools as well. In other words, int (and other number types such as float), tuple, bool, and str objects are immutable.

Let's test this hypothesis. What happens if we create a tuple object, and then give it a different value?

>>> my_tuple = (1, 2, 3) >>> id(my_tuple) 54263304 >>> my_tuple = (3, 4, 5) >>> id(my_tuple) 56898184

Just like an int object, we can see that our assignment actually changed the object that the name my_tuple is bound to.

What happens if we try to change one of the tuple's elements?

>>> my_tuple[0] = 'a new value' Traceback (most recent call last): File "", line 1, in  TypeError: 'tuple' object does not support item assignment

As we can see, Python doesn't allow us to modify my_tuple's contents, as it is immutable.

Mutable objects in Python

Some types in Python can be modified after creation, and they are called mutable. For example, we know that we can modify the contents of a list object:

>>> my_list = [1, 2, 3] >>> my_list[0] = 'a new value' >>> my_list ['a new value', 2, 3]

Does that mean we actually created a new object when assigning a new value to the first element of my_list? Again, we can use id to check:

>>> my_list = [1, 2, 3] >>> id(my_list) 55834760 >>> my_list [1, 2, 3] >>> my_list[0] = 'a new value' >>> id(my_list) 55834760 >>> my_list ['a new value', 2, 3]

So our first assignment my_list = [1, 2, 3] created an object in the address 55834760, with the values of 1, 2, and 3:

We then modified the first element of this list object using my_list[0] = 'a new value', that is - without creating a new list object:

Now, let us create two names – x and y, both bound to the same list object. We can verify that either by using is, or by explicitly checking their ids:

>>> x = y = [1, 2] >>> x is y True >>> id(x) 18349096 >>> id(y) 18349096 >>> id(x) == id(y) True

What happens now if we use x.append(3)? That is, if we add a new element (3) to the object by the name of x?

Will x by changed? Will y?

Well, as we already know, they are basically two names of the same object:

Since this object is changed, when we check its names we can see the new value:

>>> x.append(3) >>> x [1, 2, 3] >>> y [1, 2, 3]

Note that x and y have the same id as before – as they are still bound to the same list object:

>>> id(x) 18349096 >>> id(y) 18349096

In addition to lists, other Python types that are mutable include sets and dicts.

Implications for dictionary keys in Python

Dictionaries (dict objects) are commonly used in Python. As a quick reminder, we define them like so:

my_dict = {"name": "Omer", "number_of_pets": 1}

We can then access a specific element by its key name:

>>> my_dict["name"] 'Omer'

Dictionaries are mutable, so we can change their content after creation. At any given moment, a key in the dictionary can point to one element only:

>>> my_dict["name"] = "John" >>> my_dict["name"] 'John'

It is interesting to note that a dictionary's keys must be immutable:

>>> my_dict = {[1,2]: "Hello"} Traceback (most recent call last): File "", line 1, in  TypeError: unhashable type: 'list'

Why is that so?

Let's consider the following hypothetical scenario (note: the snippet below can't really be run in Python):

>>> x = [1, 2] >>> y = [1, 2, 3] >>> my_dict = {x: 'a', y: 'b'}

So far, things don't seem that bad. We'd assume that if we access my_dict with the key of [1, 2], we will get the corresponding value of 'a', and if we access the key [1, 2, 3], we will get the value 'b'.

Now, what would happen if we attempted to use:

>>> x.append(3)

In this case, x would have the value of [1, 2, 3], and y would also have the value of [1, 2, 3]. What should we get when we ask for my_dict[[1, 2, 3]]? Will it be 'a' or 'b'? To avoid such cases, Python simply doesn't allow dictionary keys to be mutable.

Taking things a bit further

Let's try to apply our knowledge to a case that is a bit more interesting.

Below, we define a list (a mutable object) and a tuple (an immutable object). The list includes a tuple, and the tuple includes a list:

>>> my_list = [(1, 1), 2, 3] >>> my_tuple = ([1, 1], 2, 3) >>> type(my_list)  >>> type(my_list[0])  >>> type(my_tuple)  >>> type(my_tuple[0]) 

So far so good. Now, try to think for yourself – what will happen when we try to execute each of the following statements?

(1) >>> my_list[0][0] = 'Changed!'

(2) >>> my_tuple[0][0] = 'Changed!'

In statement (1), what we are trying to do is change my_list's first element, that is, a tuple. Since a tuple is immutable, this attempt is destined to fail:

>>> my_list[0][0] = 'Changed!' Traceback (most recent call last): File "", line 1, in  TypeError: 'tuple' object does not support item assignment

Note that what we were trying to do is not change the list, but rather – change the contents of its first element.

Let's consider statement (2). In this case, we are accessing my_tuple's first element, which happens to be a list, and modify it. Let's further investigate this case and look at the addresses of these elements:

>>> my_tuple = ([1, 1], 2, 3) >>> id(my_tuple) 20551816 >>> type(my_tuple[0])  >>> id(my_tuple[0]) 20446248

When we change my_tuple[0][0], we do not really change my_tuple at all! Indeed, after the change, my_tuple's first element will still be the object whose address in memory is 20446248. We do, however, change the value of that object:

>>> my_tuple[0][0] = 'Changed!' >>> id(my_tuple) 20551816 >>> id(my_tuple[0]) 20446248 >>> my_tuple (['Changed!', 1], 2, 3)

Since we only modified the value of my_tuple[0], which is a mutable list object, this operation was indeed allowed by Python.

Recap

In this post we learned about Python objects. We said that in Python everything is an object, and got to use id and is to deepen our understanding of what's happening under the hood when using Python to create and modify objects.

We also learned the difference between mutable objects, that can be modified after creation, and immutable objects, which cannot.

We saw that when we ask Python to modify an immutable object that is bound to a certain name, we actually create a new object and bind that name to it.

We then learned why dictionary keys have to be immutable in Python.

Understanding how Python "sees" objects is a key to becoming a better Python programmer. I hope this post has helped you on your journey to mastering Python.

Omer Rosenbaum, Swimm’s Chief Technology Officer. Cyber training expert and Founder of Checkpoint Security Academy. Author of Computer Networks (in Hebrew). Visit My YouTube Channel.