Si paso un marco de datos a una función y lo modifico dentro de la función, ¿es paso por valor o paso por referencia?
Ejecuto el siguiente código
a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
def letgo(df):
df = df.drop('b',axis=1)
letgo(a)
el valor de a
no cambia después de la llamada a la función. ¿Significa que se pasa por valor?
También probé lo siguiente
xx = np.array([[1,2], [3,4]])
def letgo2(x):
x[1,1] = 100
def letgo3(x):
x = np.array([[3,3],[3,3]])
Resulta letgo2()
que cambia xx
y letgo3()
no. ¿Por qué es como este?
Respuestas:
La respuesta corta es, Python siempre pasa por valor, pero cada variable de Python es en realidad un puntero a algún objeto, por lo que a veces parece pasar por referencia.
En Python, cada objeto es mutable o no mutable. por ejemplo, listas, dictados, módulos y marcos de datos de Pandas son mutables, y las entradas, cadenas y tuplas no son mutables. Los objetos mutables se pueden cambiar internamente (por ejemplo, agregar un elemento a una lista), pero los objetos no mutables no.
Como dije al principio, puede pensar en cada variable de Python como un puntero a un objeto. Cuando pasa una variable a una función, la variable (puntero) dentro de la función es siempre una copia de la variable (puntero) que se pasó. Entonces, si asigna algo nuevo a la variable interna, todo lo que está haciendo es cambiar el variable local para apuntar a un objeto diferente. Esto no altera (muta) el objeto original al que apuntaba la variable, ni hace que la variable externa apunte al nuevo objeto. En este punto, la variable externa todavía apunta al objeto original, pero la variable interna apunta a un nuevo objeto.
Si desea alterar el objeto original (solo es posible con tipos de datos mutables), debe hacer algo que altere el objeto sin asignar un valor completamente nuevo a la variable local. Esta es la razón
letgo()
yletgo3()
dejar el elemento externo inalterado, peroletgo2()
lo altera.Como señaló @ursan, si se
letgo()
usa algo como esto, entonces alteraría (mutaría) el objeto original al quedf
apunta, lo que cambiaría el valor visto a través de laa
variable global :def letgo(df): df.drop('b', axis=1, inplace=True) a = pd.DataFrame({'a':[1,2], 'b':[3,4]}) letgo(a) # will alter a
En algunos casos, puede vaciar completamente la variable original y rellenarla con nuevos datos, sin hacer una asignación directa, por ejemplo, esto alterará el objeto original al que
v
apunta, lo que cambiará los datos que se ven cuando usev
más adelante:def letgo3(x): x[:] = np.array([[3,3],[3,3]]) v = np.empty((2, 2)) letgo3(v) # will alter v
Observe que no estoy asignando algo directamente a
x
; Estoy asignando algo a todo el rango interno dex
.Si es absolutamente necesario crear un objeto completamente nuevo y hacerlo visible externamente (que a veces es el caso de los pandas), tiene dos opciones. La opción 'limpiar' sería simplemente devolver el nuevo objeto, por ejemplo,
def letgo(df): df = df.drop('b',axis=1) return df a = pd.DataFrame({'a':[1,2], 'b':[3,4]}) a = letgo(a)
Otra opción sería llegar fuera de su función y alterar directamente una variable global. Esto cambia
a
para apuntar a un nuevo objeto, y cualquier función a la que se refieraa
posteriormente verá ese nuevo objeto:def letgo(): global a a = a.drop('b',axis=1) a = pd.DataFrame({'a':[1,2], 'b':[3,4]}) letgo() # will alter a!
Alterar directamente las variables globales suele ser una mala idea, porque cualquiera que lea su código tendrá dificultades para averiguar cómo
a
se modificó. (Por lo general, uso variables globales para parámetros compartidos utilizados por muchas funciones en un script, pero no dejo que alteren esas variables globales).fuente
La pregunta no es PBV vs. PBR. Estos nombres solo causan confusión en un lenguaje como Python; fueron inventados para lenguajes que funcionan como C o como Fortran (como lenguajes PBV y PBR por excelencia). Es cierto, pero no esclarecedor, que Python siempre pasa por valor. La pregunta aquí es si el valor en sí está mutado o si obtiene un nuevo valor. Los pandas suelen pecar de este último.
http://nedbatchelder.com/text/names.html explica muy bien cuál es el sistema de nombres de Python.
fuente
Para agregar a la respuesta de @Mike Graham, quien señaló una muy buena lectura:
En su caso, lo que es importante recordar es la diferencia entre nombres y valores .
a
,df
,xx
,x
, Son todos nombres , pero se refieren a los mismos o diferentes valores en diferentes puntos de sus ejemplos:En el primer ejemplo,
letgo
vuelvedf
a enlazar a otro valor, porquedf.drop
devuelve un nuevo aDataFrame
menos que establezcas el argumentoinplace = True
( ver doc ). Eso significa que el nombredf
(local a laletgo
función), que se refería al valor dea
, ahora se refiere a un nuevo valor, aquí eldf.drop
valor de retorno. El valor al quea
se refiere todavía existe y no ha cambiado.En el segundo ejemplo,
letgo2
mutax
, sin volver a unirlo, por lo quexx
se modifica porletgo2
. A diferencia del ejemplo anterior, aquí el nombre localx
siempre se refiere al valor al quexx
se refiere el nombre y cambia ese valor en su lugar , por lo que el valor al quexx
se refiere ha cambiado.En el tercer ejemplo, se
letgo3
vuelvex
a vincular a un nuevonp.array
. Eso hace que el nombrex
, localletgo3
y anteriormente referido al valor dexx
, ahora se refiera a otro valor, el nuevonp.array
. El valor al quexx
se refiere no ha cambiado.fuente
Python no se pasa por valor ni pasa por referencia. Se pasa por asignación.
Referencia de apoyo, las preguntas frecuentes de Python: https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
OIA:
Entonces, si pasa una lista y cambia su valor 0, ese cambio se ve tanto en el llamado como en el llamador. Pero si reasigna la lista con una nueva lista, este cambio se pierde. Pero si se mire la lista y reemplazar que con una nueva lista, que el cambio es visto tanto en la llamada y la persona que llama.
P.EJ:
def change_it(list_): # This change would be seen in the caller if we left it alone list_[0] = 28 # This change is also seen in the caller, and replaces the above # change list_[:] = [1, 2] # This change is not seen in the caller. # If this were pass by reference, this change too would be seen in # caller. list_ = [3, 4] thing = [10, 20] change_it(thing) # here, thing is [1, 2]
Si eres un fanático de C, puedes pensar en esto como pasar un puntero por valor, no un puntero a un puntero a un valor, solo un puntero a un valor.
HTH.
fuente
Aquí está el documento para soltar:
Entonces se crea un nuevo marco de datos. El original no ha cambiado.
Pero como para todos los objetos en Python, el marco de datos se pasa a la función por referencia.
fuente
df
dentro de la función, ¿no significa que el valor al que se hace referencia se ha cambiado al nuevo objeto?necesita hacer 'a' global al comienzo de la función, de lo contrario es una variable local y no cambia la 'a' en el código principal.
fuente