Aprende leyendo en orden

Argumentos de función y referencias a objetos — los mutables se modifican dentro de las funciones

Comprende cómo se comportan los argumentos de función en Python según sean mutables o inmutables, y evita las trampas comunes.

Cuando llamas a una función, el comportamiento cambia dependiendo de si el argumento es un tipo mutable (list / dict / set) o un tipo inmutable (int / str / tuple). Este artículo recorre esa diferencia y los patrones básicos para escribir funciones seguras.

Argumentos inmutables — los cambios internos no se filtran fuera

Los enteros, flotantes, cadenas y tuplas son inmutables (no se pueden cambiar en el sitio). Recibe uno como argumento y reescríbelo con += 10 o = otro_valor, y la variable del llamador queda intacta.

Por debajo, += 10 crea un nuevo valor y reasigna solo el nombre del argumento interno — el nombre del llamador sigue apuntando al original.

Argumentos inmutables
llamadorx = 5en la funciónx = 5x = 5(sin cambios)x = 15(nuevo valor)pasarx += 10
def try_modify_number(value):
    value += 10
    print(f"dentro: {value}")

x = 5
try_modify_number(x)
print(f"fuera: {x}")

# dentro: 15
# fuera: 5   <- sigue siendo el original

# Lo mismo pasa con cadenas y tuplas
def try_modify_text(text):
    text = text + " world"
    print(f"dentro: {text}")

message = "hello"
try_modify_text(message)
print(f"fuera: {message}")   # hello

Escribe una función para añadir impuestos y confirma que cambiar el argumento int no afecta al exterior.

① Define def add_tax(price):, asigna price = int(price * 1.1), luego imprime f"dentro: {price}". (Esta vez sin valor de retorno.)

② Asigna base_price = 1000, llama a add_tax(base_price), luego imprime f"fuera: {base_price}".

Éxito significa que el base_price exterior no cambia.

(La explicación aparece una vez que se ejecuta correctamente.)

Editor Python

Ejecutar el código para ver el resultado

Argumentos mutables — los cambios internos se filtran fuera

Por otro lado, cuando pasas un tipo mutable como list / dict / set y llamas a algo como .append() o .update() para modificar el contenido en el sitio, la variable del llamador ve el mismo cambio.

Eso se debe a que en el momento en que se pasa el argumento, el nombre del llamador y el nombre del argumento interno comparten la misma caja. Es el mismo mecanismo que y = x del artículo anterior sobre mutables vs inmutables — ahora ocurriendo en el límite de la llamada.

Argumentos mutables
llamadormy_list = [1, 2, 3]en la funciónitems = [1, 2, 3]my_list =[1, 2, 3, 100](también cambia)items =[1, 2, 3, 100]pasa la misma cajaitems.append(100)reflejado
def try_modify_list(items):
    items.append(100)
    print(f"dentro: {items}")

my_list = [1, 2, 3]
try_modify_list(my_list)
print(f"fuera: {my_list}")

# dentro: [1, 2, 3, 100]
# fuera: [1, 2, 3, 100]   <- el exterior también cambió

# Lo mismo con dicts
def set_role(user, role):
    user["role"] = role

admin = {"name": "Ana"}
set_role(admin, "admin")
print(admin)   # {'name': 'Ana', 'role': 'admin'}

Cuando las mutaciones internas se filtran fuera, la causa es difícil de rastrear

cart.append(...) parece perfectamente intencional en su propia línea. Pero como el append dentro de la función también alcanza al my_list exterior, puedes acabar con síntomas del tipo «la lista creció en silencio» que aparecen en algún lugar completamente no relacionado.

Siente de primera mano cómo los datos exteriores se modifican sin querer.

① Define def add_item(cart, item):, ejecuta cart.append(item), luego imprime f"dentro: {cart}". (Sin return.)

② Prepara my_cart = ["leche", "pan"], llama a add_item(my_cart, "huevos"), luego imprime f"fuera: {my_cart}".

Confirma que "huevos" también termina en el my_cart exterior (esta es la diferencia con el caso inmutable de la sección anterior).

Editor Python

Ejecutar el código para ver el resultado

Modificar solo dentro de la función — protege los argumentos con .copy()

Cuando quieras dejar los datos del llamador en paz y solo cambiar cosas dentro de la función, basta con poner .copy() al comienzo de la función. Copia la lista, dict o set entrante en una caja separada antes de modificarla, y el llamador no se ve afectado.

Devuelve la versión modificada con return, y el llamador puede tener al mismo tiempo el carrito original y el nuevo carrito. Una vez que este patrón sea memoria muscular, estarás escribiendo funciones seguras y libres de efectos secundarios (sin modificación del llamador).

El flujo completo: ① copia el argumento con .copy() en una caja separada → ② edita la copia → ③ devuelve el resultado. Memoriza estos tres pasos y podrás diseñar de forma segura cualquier función que tome argumentos mutables.

Crea una nueva caja con .copy() antes de modificar
items = cart.copy()items.append(x)el cart originalse mantiene igualsin efecto
def add_item_safely(cart, item):
    items = cart.copy()    # copia en una caja separada antes de editar
    items.append(item)
    return items           # devuelve el resultado con return

my_cart = ["leche", "pan"]
new_cart = add_item_safely(my_cart, "huevos")
print(my_cart)   # ['leche', 'pan']                <- intacto
print(new_cart)  # ['leche', 'pan', 'huevos']      <- una lista separada

Reescribe la función de la sección anterior en una versión segura que no modifique al llamador.

① Define def add_item_safely(cart, item):, copia la entrada con items = cart.copy(), ejecuta items.append(item) y termina con return items.

② Prepara my_cart = ["leche", "pan"] y captura el resultado con new_cart = add_item_safely(my_cart, "huevos").

③ Imprime tanto my_cart como new_cart y confirma que el my_cart original no cambia mientras que solo new_cart tiene "huevos" añadido.

Editor Python

Ejecutar el código para ver el resultado
QUIZ

Verificación de conocimientos

Responde cada pregunta una a una.

Pregunta 1Después de ejecutar este código, ¿cuál es el valor de x?
def f(value):
value += 10

x = 5
f(x)
print(x)

Pregunta 2Después de ejecutar este código, ¿cuál es el valor de my_list?
def g(items):
items.append(100)

my_list = [1, 2, 3]
g(my_list)
print(my_list)

Pregunta 3Cuando quieres devolver una nueva lista sin modificar la lista del llamador, ¿qué es lo primero que deberías hacer?