Logotipo de Zephyrnet

5 errores comunes de Python (y cómo evitarlos) – KDnuggets

Fecha:

5 errores comunes de Python (y cómo evitarlos)
Imagen del autor
 

Python es un lenguaje de programación versátil y apto para principiantes conocido por su simplicidad y legibilidad. Su elegante sintaxis, sin embargo, no es inmune a peculiaridades que pueden sorprender incluso a los desarrolladores de Python experimentados. Y comprenderlos es esencial para escribir código libre de errores o, por así decirlo, depurarlo sin complicaciones.

Este tutorial explora algunos de estos errores: valores predeterminados mutables, alcance variable en bucles y comprensiones, asignación de tuplas y más. Codificaremos ejemplos simples para ver porque las cosas funcionan como lo hacen, y también miramos cómo podemos evitarlos (si realmente podemos 🙂). 

¡Entonces empecemos!

En Python, los valores predeterminados mutables son esquinas afiladas comunes. Te encontrarás con un comportamiento inesperado cada vez que definas una función con objetos mutables, como listas o diccionarios, como argumentos predeterminados. 

El valor predeterminado se evalúa solo una vez, cuando se define la función, y no cada vez que se llama a la función.. Esto puede provocar un comportamiento inesperado si modifica el argumento predeterminado dentro de la función.

Tomemos un ejemplo:

def add_to_cart(item, cart=[]):
    cart.append(item)
    return cart

 

En este ejemplo, add_to_cart es una función que toma un elemento y lo agrega a una lista cart. El valor predeterminado de cart es una lista vacía. Es decir, llamar a la función sin un artículo para agregar devuelve un carrito vacío. 

Y aquí hay un par de llamadas a funciones:

# User 1 adds items to their cart
user1_cart = add_to_cart("Apple")
print("User 1 Cart:", user1_cart)  

 

Output >>> ['Apple']

 

Esto funciona como se esperaba. ¿Pero qué pasa ahora?

# User 2 adds items to their cart
user2_cart = add_to_cart("Cookies")
print("User 2 Cart:", user2_cart) 

 

Output >>>

['Apple', 'Cookies'] # User 2 never added apples to their cart!

 

Debido a que el argumento predeterminado es una lista (un objeto mutable), conserva su estado entre llamadas a funciones. Así que cada vez que llames add_to_cart, agrega el valor al mismo objeto de lista creado durante la definición de la función. En este ejemplo, es como si todos los usuarios compartieran el mismo carrito.

Como evitar

Como solución alternativa, puede configurar cart a None e inicializa el carrito dentro de la función así:

def add_to_cart(item, cart=None):
    if cart is None:
        cart = []
    cart.append(item)
    return cart

 

Entonces cada usuario ahora tiene un carrito separado. 🙂

Si necesita un repaso sobre las funciones de Python y los argumentos de las funciones, lea Argumentos de funciones de Python: una guía definitiva.

Las rarezas del alcance de Python requieren un tutorial propio. Pero veremos una de esas rarezas aquí.

Mire el siguiente fragmento:

x = 10
squares = []
for x in range(5):
    squares.append(x ** 2)

print("Squares list:", squares)  

# x is accessible here and is the last value of the looping var
print("x after for loop:", x)

 

La variable x se establece en 10. Pero x también es la variable de bucle. Pero asumiríamos que el alcance de la variable de bucle está limitado al bloque de bucle for, ¿no?

Veamos el resultado:

Output >>>

Squares list: [0, 1, 4, 9, 16]
x after for loop: 4

 

Vemos eso x ahora es 4, el valor final que toma en el bucle, y no el valor inicial de 10 que le asignamos.

Ahora veamos qué sucede si reemplazamos el bucle for con una expresión de comprensión:

x = 10
squares = [x ** 2 for x in range(5)]

print("Squares list:", squares)  

# x is 10 here
print("x after list comprehension:", x)

 

Aquí, x es 10, el valor que le asignamos antes de la expresión de comprensión:

Output >>>

Squares list: [0, 1, 4, 9, 16]
x after list comprehension: 10

Como evitar

Para evitar comportamientos inesperados: si utiliza bucles, asegúrese de no nombrar la variable de bucle igual que otra variable a la que desee acceder más adelante.

En Python, usamos el is palabra clave para comprobar la identidad del objeto. Lo que significa que comprueba si dos variables hacen referencia al mismo objeto en la memoria. Y para verificar la igualdad, usamos el == operador. ¿Sí?

Ahora, inicie un REPL de Python y ejecute el siguiente código:

>>> a = 7
>>> b = 7
>>> a == 7
True
>>> a is b
True

 

Ahora ejecuta esto:

>>> x = 280
>>> y = 280
>>> x == y
True
>>> x is y
False

 

Espera, ¿por qué sucede esto? Bueno, esto se debe al "almacenamiento en caché de enteros" o "internado" en CPython, la implementación estándar de Python.

CPython almacena en caché objetos enteros en el rango de -5 a 256. Es decir, cada vez que use un número entero dentro de este rango, Python usará el mismo objeto en la memoria. Por lo tanto, cuando comparas dos números enteros dentro de este rango usando el is palabra clave, el resultado es True porque referirse al mismo objeto en la memoria.

Es por eso que a is b devoluciones True. También puedes verificar esto imprimiendo id(a) y id(b).

Sin embargo, los números enteros fuera de este rango no se almacenan en caché. Y cada aparición de tales números enteros crea un nuevo objeto en la memoria. 

Entonces, cuando comparas dos números enteros fuera del rango almacenado en caché usando el is palabra clave (sí, x y y ambos configurados en 280 en nuestro ejemplo), el resultado es False porque de hecho son dos objetos diferentes en la memoria.

Como evitar

Este comportamiento no debería ser un problema a menos que intente utilizar el is para comparar la igualdad de dos objetos. Así que siempre usa el == operador para comprobar si dos objetos de Python tienen el mismo valor.

Si está familiarizado con las estructuras de datos integradas en Python, sabrá que las tuplas son inmutable. Vos tambien no puede modificarlos en su lugar. Por otro lado, las estructuras de datos como listas y diccionarios son mudable. Es decir, tu podemos cámbielos en su lugar.

Pero ¿qué pasa con las tuplas que contienen uno o más objetos mutables?

Es útil iniciar un REPL de Python y ejecutar este sencillo ejemplo:

>>> my_tuple = ([1,2],3,4)
>>> my_tuple[0].append(3)
>>> my_tuple
([1, 2, 3], 3, 4)

 

Aquí, el primer elemento de la tupla es una lista con dos elementos. ¡Intentamos agregar 3 a la primera lista y funciona bien! Bueno, ¿acabamos de modificar una tupla en su lugar?

Ahora intentemos agregar dos elementos más a la lista, pero esta vez usando el operador +=:

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

 

Sí, obtienes un TypeError que dice que el objeto tupla no admite la asignación de elementos. Lo cual se espera. Pero revisemos la tupla: 

>>> my_tuple
([1, 2, 3, 4, 5], 3, 4)

 

¡Vemos que se han agregado los elementos 4 y 5 a la lista! ¿El programa simplemente arrojó un error y tuvo éxito al mismo tiempo?

Bueno, el operador += funciona internamente llamando al __iadd__() método que realiza la suma in situ y modifica la lista in situ. La asignación genera una excepción TypeError, pero la adición de elementos al final de la lista ya se realizó correctamente. += ¡es quizás la esquina más aguda!

Como evitar

Para evitar este tipo de peculiaridades en su programa, intente usar tuplas , solamente para colecciones inmutables. Y evite el uso de objetos mutables como elementos de tupla tanto como sea posible.

La mutabilidad ha sido un tema recurrente en nuestra discusión hasta ahora. Así que aquí hay otro para concluir este tutorial.

A veces es posible que necesites crear copias independientes de listas. Pero, ¿qué sucede cuando creas una copia usando una sintaxis similar a list2 = list1 donde list1 cual es la lista original?

Es una copia superficial que se crea. Por lo que sólo copia las referencias a los elementos originales de la lista. La modificación de elementos a través de la copia superficial afectará ambas la lista original y la copia superficial. 

Tomemos este ejemplo:

original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Shallow copy of the original list
shallow_copy = original_list

# Modify the shallow copy
shallow_copy[0][0] = 100

# Print both the lists
print("Original List:", original_list)
print("Shallow Copy:", shallow_copy)

 

Vemos que los cambios en la copia superficial también afectan a la lista original:

Output >>>

Original List: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]
Shallow Copy: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]

 

Aquí modificamos el primer elemento de la primera lista anidada en la copia superficial: shallow_copy[0][0] = 100. Pero vemos que la modificación afecta tanto a la lista original como a la copia superficial. 

Como evitar

Para evitar esto, puedes crear una copia profunda como esta:

import copy

original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

# Deep copy of the original list
deep_copy = copy.deepcopy(original_list)

# Modify an element of the deep copy
deep_copy[0][0] = 100

# Print both lists
print("Original List:", original_list)
print("Deep Copy:", deep_copy)

 

Ahora, cualquier modificación en la copia profunda deja la lista original sin cambios.

Output >>>

Original List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Deep Copy: [[100, 2, 3], [4, 5, 6], [7, 8, 9]]

¡Y eso es una envoltura! En este tutorial, hemos explorado varias rarezas en Python: desde el sorprendente comportamiento de los valores predeterminados mutables hasta las sutilezas de la copia superficial de listas. Esta es sólo una introducción a las rarezas de Python y de ninguna manera es una lista exhaustiva. Puedes encontrar todos los ejemplos de código. en GitHub.

A medida que sigas codificando durante más tiempo en Python y comprendas mejor el lenguaje, quizás te encuentres con muchos más de estos. Entonces, ¡sigue codificando, sigue explorando!

Ah, y déjanos saber en los comentarios si quieres leer una secuela de este tutorial.
 
 

Bala Priya C. es un desarrollador y escritor técnico de la India. Le gusta trabajar en la intersección de matemáticas, programación, ciencia de datos y creación de contenido. Sus áreas de interés y experiencia incluyen DevOps, ciencia de datos y procesamiento del lenguaje natural. ¡Le gusta leer, escribir, codificar y tomar café! Actualmente, está trabajando para aprender y compartir sus conocimientos con la comunidad de desarrolladores mediante la creación de tutoriales, guías prácticas, artículos de opinión y más. Bala también crea atractivas descripciones de recursos y tutoriales de codificación.

punto_img

Información más reciente

punto_img