Entender cómo Python pasa argumentos a las funciones es crucial para prevenir cambios inesperados en los datos y para un correcto diseño del código.
En lenguajes de programación tradicionales como C o Java, se utiliza la transmisión por valor (copy by value) o por referencia (copy by reference). Sin embargo, Python tiene un modelo diferente: call by object reference (a veces llamado "call by sharing").
Muchos desarrolladores erróneamente piensan que Python siempre pasa argumentos por referencia o por valor. Esto inevitablemente conduce a situaciones en las que los objetos mutables se modifican inesperadamente en el código que llama.
En Python, los valores de los parámetros de la función son referencias a los objetos que se pasan a la función. Esto significa:
Ejemplo:
# list - mutable def add_item(lst): lst.append(42) my_list = [1, 2, 3] add_item(my_list) print(my_list) # [1, 2, 3, 42] # int - inmutable def add_num(x): x = x + 1 num = 10 add_num(num) print(num) # 10
Características clave:
¿Se pasan siempre argumentos por referencia en Python?
No, en Python se pasan referencias a los objetos, y cómo se comporta el objeto depende de si es mutable o no. Un objeto inmutable creará un nuevo objeto con cualquier cambio.
¿Se puede reasignar un argumento mutable en la función de manera que afecte al objeto externo?
No. Si dentro de la función asignas un nuevo valor a un parámetro, el objeto externo no cambiará — solo estás cambiando la referencia local.
Ejemplo:
def reassign_list(lst): lst = [99, 100] my_list = [1, 2, 3] reassign_list(my_list) print(my_list) # [1, 2, 3]
¿Por qué una función que recibe una lista por defecto puede comportarse de manera extraña en llamadas sucesivas?
Porque el valor por defecto se crea una sola vez — al definir la función, y si se modifica (por ejemplo, añadiendo un elemento), se cambiará para todas las llamadas posteriores.
def add_element(x, cache=[]): cache.append(x) return cache print(add_element(1)) # [1] print(add_element(2)) # [1, 2]
Un programador pasa una lista a la función y espera que su lista original no cambie, pero la función añade un elemento.
Pros:
Contras:
Un programador copia explícitamente la lista dentro de la función, si necesita devolver algo, pero no cambiar el original:
def process_data(data): data = data.copy() # o list(data) # trabajo seguro con la copia data.append('informe') return data
Pros:
Contras: