La función cambia los valores de la lista y los valores no variables en Python

Tomemos un código simple:

y = [1,2,3] def plusOne(y): for x in range(len(y)): y[x] += 1 return y print plusOne(y), y a = 2 def plusOne2(a): a += 1 return a print plusOne2(a), a 

Los valores de ‘y’ cambian pero el valor ‘a’ se mantiene igual. Ya aprendí que es porque uno es mutable y el otro no. Pero, ¿cómo cambiar el código para que la función no cambie la lista?

Por ejemplo, para hacer algo así (en pseudocódigo para simplificar):

 a = [1,2,3,...,n] function doSomething(x): do stuff with x return x b = doSomething(a) if someOperation(a) > someOperation(b): do stuff 

EDITAR: Lo siento, pero tengo otra pregunta en las listas anidadas . Ver este codigo

 def change(y): yN = y[:] for i in range(len(yN)): if yN[i][0] == 1: yN[i][0] = 0 else: yN[i][0] = 1 return yN data1 = [[1],[1],[0],[0]] data2 = change(data1) 

Aquí no funciona. ¿Por qué? De nuevo: ¿cómo evitar este problema? Entiendo por qué no funciona: yN = y [:] copia los valores de y a yN, pero los valores también son listas, por lo que la operación debería duplicarse para cada lista en la lista. ¿Cómo hacer esta operación con listas anidadas?

Las variables de Python contienen punteros, o referencias, a objetos. Todos los valores (incluso los enteros) son objetos, y la asignación cambia la variable para que apunte a un objeto diferente. No almacena un nuevo valor en la variable, cambia la variable para referirse o apuntar a un objeto diferente. Por esta razón, mucha gente dice que Python no tiene “variables”, tiene “nombres” y la operación = no asigna un valor a una variable, sino que “une un nombre a un objeto”.

En plusOne estás modificando (o “mutando”) el contenido de y pero nunca cambias a lo que y sí se refiere. Permanece apuntando a la misma lista, la que usted pasó a la función. La variable global y y la variable local y refieren a la misma lista, por lo que los cambios son visibles usando cualquiera de las dos variables. Dado que cambió el contenido del objeto que se pasó, en realidad no hay razón para devolver y (de hecho, devolver None es lo que Python hace para operaciones como esta que modifican una lista “en su lugar”: los valores son devueltos por operaciones que crean nuevos objetos en lugar de mutar los existentes).

En plusOne2 está cambiando la variable local a para referirse a un objeto entero diferente, 3 . (“Vinculando el nombre a al objeto 3 “.) La variable global a no se cambia por esto y continúa apuntando a 2 .

Si no desea cambiar una lista pasada, haga una copia de la misma y cámbiela. Luego, su función debería devolver la nueva lista, ya que es una de esas operaciones que crea un nuevo objeto, y el nuevo objeto se perderá si no lo devuelve. Puede hacer esto como la primera línea de la función: x = x[:] por ejemplo (como han señalado otros). O, si puede ser útil que la función se llame de cualquier manera, puede hacer que la persona que llama pase x[:] si quiere que se haga una copia.

Crea una copia de la lista. Utilizando testList = inputList[:] . Ver el codigo

 >>> def plusOne(y): newY = y[:] for x in range(len(newY)): newY[x] += 1 return newY >>> y = [1, 2, 3] >>> print plusOne(y), y [2, 3, 4] [1, 2, 3] 

O bien, puede crear una nueva lista en la función

 >>> def plusOne(y): newList = [] for elem in y: newList.append(elem+1) return newList 

También puede utilizar una comprensión como otros han señalado.

 >>> def plusOne(y): return [elem+1 for elem in y] 

Puede pasar una copia de su lista, usando notación de corte:

 print plusOne(y[:]), y 

O la mejor manera sería crear la copia de la lista en la propia función, para que la persona que llama no tenga que preocuparse por la posible modificación:

 def plusOne(y): y_copy = y[:] 

y trabajar en y_copy en y_copy lugar.


O como lo señaló @abarnet en los comentarios, puede modificar la función para usar la list comprehension , lo que creará una nueva lista por completo:

 return [x + 1 for x in y] 

Simplemente cree una nueva lista con los valores que desea y devuélvala.

 def plus_one(sequence): return [el + 1 for el in sequence] 

Como han señalado otros, debe utilizar newlist = original[:] o newlist = list(original) para copiar la lista si no desea modificar el original.

 def plusOne(y): y2 = list(y) # copy the list over, y2 = y[:] also works for i, _ in enumerate(y2): y2[i] += 1 return y2 

Sin embargo, puede lograr su salida deseada con una lista de comprensión

 def plusOne(y): return [i+1 for i in y] 

Esto recorrerá los valores en y y creará una nueva lista agregando uno a cada uno de ellos.

Para responder a su pregunta editada:

Copiar estructuras de datos anidadas se denomina copia profunda . Para hacer esto en Python, use deepcopy() dentro del módulo de copy .

Puede hacerlo haciendo una función y llamar a esta función mediante la función de mapa, la función de mapa llamará a la función de agregar y le dará el valor y luego imprimirá el nuevo valor así:

 def add(x): return x+x print(list(map(add,[1,2,3]))) 

O puede usar la función (* rango), es muy fácil hacerlo así como ese ejemplo:

 print ([i+i for i in [*range (1,10)]])