2011-08-26 20 views
6

Tengo una pregunta sobre la función map en Python.Función de mapa Python, pasando por referencia/valor?

Por lo que entiendo, la función no muta la lista en la que está operando, sino que crea una nueva y la devuelve. Es esto correcto ?

Además, tengo el siguiente fragmento de código

def reflect(p,dir): 
    if(dir == 'X'): 
    func = lambda (a,b) : (a * -1, b) 
    else: 
    func = lambda (a,b) : (a, b * -1) 
    p = map(func,p) 
    print 'got', p 

points es una matriz de tuplas como: [(1, 1), (-1, 1), (-1, -1), (1, -1)]

Si llamo la función anterior de tal manera:

print points 
reflect(points,'X') 
print points 

la lista points no cambia. Sin embargo, dentro de la función, la función de impresión imprime correctamente lo que quiero.

¿Podría alguien señalarme en alguna dirección donde podría aprender cómo funciona todo esto pasando por el valor/referencia, etc. en python, y cómo podría solucionar lo anterior? O tal vez estoy tratando demasiado duro para emular Haskell en Python ...

Gracias

edición:

decir en lugar de p = map(func,p) Yo

for i in range(len(p)): 
    p[i] = func(p[i]) 

El valor de la lista es actualizado fuera de la función, como si trabajara por referencia. Ugh, espero que esto esté claro: S

+0

¿Podría explicar un poco más explícitamente cuál es su pregunta/problema? La referencia a Haskel no es muy útil para los programadores que no son Haskel. – Achim

+0

Hola.Sí, lo siento por esto, tal vez simplemente ignore el comentario de Haskell. Me sorprende que aunque hago 'p = map (func, p)', la lista p no se cambia fuera de la función. Si reemplazo 'p = map (func, p)' con una iteración de lista, parece que funciona. – rafalio

+0

Python no pasa por valor ni pasa por referencia, python pasa por objeto. –

Respuesta

12

No entiendes cómo funcionan las referencias en Python. Aquí, todos los nombres son referencias, no hay "valores". Los nombres están ligados a los objetos. Pero = no modifica el objeto que está apuntado por el nombre - se vuelve a vincular el nombre a un objeto diferente:

x = 42 
y = x 
# now: 
# 'is' is a identity operator — it checks whether two names point to the 
# exact same object 
print x is y # => True 
print x, y # 42 42 
y = 69 
# now y has been rebound, but that does not change the '42' object, nor rebinds x 
print x is y # => False 
print x, y # 42 69 

Para modificar el objeto en sí mismo, tiene que ser mutable - es decir, exponer a los miembros que mutan o tener un dict modificable Lo mismo que arriba ocurre cuando se vuelve a enlazar p - no toca points en absoluto, simplemente modifica el significado del nombre local p.

Si desea simular referencias similares a C++, debe encapsular el objeto en un contenedor mutable, p. una lista.

reflect([points], 'X') 

# inside reflect: 
p[0] = ... 

Pero no debería, al menos en este caso, simplemente debe devolver el nuevo objeto.

points = reflect(points, 'X') 

# inside reflect, instead of p = ... 
return map(func, p) 

Bueno, ahora que lo pienso, también puede hacer

p[:] = map(func, p) 

Pero, de nuevo, volviendo nuevo objeto es generalmente mejor.

+0

Ah vale, esto tiene sentido. Estaba confundido y pensé que el operador '=' funcionaba de manera diferente a como lo hace, gracias. – rafalio

0

El modelo de datos de Python se basa en una trilogía:
identificador - referencia - objeto

.

  • El identificador es un string escrito en el código.
  • La referenciaes una variable sensu stricto, es decir, "un trozo de memoria cuyo contenido puede cambiar". El valor de una referencia es la dirección del objeto.
  • El objeto tiene una implementación basada en las estructuras de la langage C que son las fundaciones de Python.

Otras palabras también se utilizan para designar el 'identificador':
1) nombre
2) variables; porque esta palabra es utilizada por la metonimia en las matemáticas para designar los símbolos que representan las variables matemáticas reales, y las variables en una computadora tienen conceptualmente el mismo funcionamiento que las variables matemáticas (sus valores pueden cambiar).
Este uso en Python es un hábito muy malo en mi opinión: crea ambigüedades y confusión con lo que se llama 'variable' en informática: "pedazo de memoria cuyo contenido puede cambiar".

Lo mejor es usar la palabra: identificador

.

Un identificador y un objeto están vinculados en un cierto espacio de nombres. Los espacios de nombres se muestran bajo la forma de diccionarios de Python, pero NO SON diccionarios.

  • la unión del identificador y el objeto es indirecta, a través de la referencia.

  • La vinculación del identificador y la referencia es directa, y se realiza en la TABLA DE SÍMBOLOS (o tabla de símbolos).

En informática, una tabla de símbolos es una estructura de datos utilizada por un traductor lenguaje como un compilador o intérprete, donde cada identificador en el código fuente de un programa está asociado con la información relativa a su declaración o aparición en la fuente, como su tipo , nivel de alcance y, en ocasiones, su ubicación.
http://en.wikipedia.org/wiki/Symbol_table

Ellos dicen: identificadores. Precisamente.
Raramente veo alusiones a la tabla de símbolos, aunque es lo más importante que arroja luz sobre el funcionamiento del modelo de datos de Python IMO.

En mi opinión, la palabra

vinculante

no designa un mecanismo preciso y único a nivel mundial, pero el conjunto de todos los mecanismos en relación con el identificador de la trilogía - Referencia - objeto

.

No pretendo entender perfectamente todas las cuestiones relativas a los datos y modelos de ejecución de Python, y que las consideraciones anteriores son las más exactas y precisas que se pueden hacer.
Sin embargo, me permiten tener una comprensión operativa de lo que sucede durante las ejecuciones de código.
Estaría muy feliz de ser corregido en algunos puntos si estoy equivocado (por ejemplo, estoy muy satisfecho de haber aprendido de Michael Foord que la naturaleza de los espacios de nombres no es el diccionario, que es solo la forma en que están representados)

.

Dicho esto, no sé lo que se llama valor y referencia cuando se discute el tema de pasar algo como argumento en Python, y tengo la impresión de que muchas de las personas que han expresado sobre el tema en numerosos las discusiones esotéricas no saben más que yo. creo que no hay una opinión mejor y lúcido sobre el tema que este uno de Alex Martelli:

"Tratar de volver a utilizar terminología que se forma más general aplicada a idiomas donde 'las variables son cajas' a un lenguaje donde "las variables son etiquetas post-it" es, en mi humilde opinión, más susceptible de confundir que de ayudar ".

Alex Martelli

http://bytes.com/topic/python/answers/37219-value-reference

+0

En respuesta a su indicador de moderador, solo las preguntas pueden tener etiquetas. Las respuestas tienen automáticamente la misma etiqueta (no se muestra) que la pregunta que responden. –

+0

@Bill the Lizard Gracias. Me di cuenta de eso después de un tiempo. Sin embargo, su respuesta es interesante porque dice que las respuestas también tienen la misma etiqueta que la pregunta, aunque la etiqueta no se muestra. Era algo que me preguntaba: ¿qué sucede cuando hacemos una búsqueda con etiquetas? Ahora sé que las etiquetas se toman en cuenta ya que las respuestas están etiquetadas. – eyquem

0

Quiero subir esto. Las respuestas realmente no responden a la pregunta: ignoran el hecho de que el OP pudo lograr el resultado deseado al iterar. La pregunta se reduce al comportamiento de map. He aquí un ejemplo más directo:

f=(lambda pair: pair[0].append(pair[1])) 
a = ([],1) 
f(a) 
print(a) #prints ([1],1) 
a=([],1) 
map(f,[a]) 
print(a) #prints ([0],1) 

Así mapa no está mutando objetos en la forma en que el OP está esperando. Tengo la misma confusión.

¿Alguien puede comentar exactamente lo que está pasando aquí? Creo que sería una buena respuesta a la pregunta del OP.

en cuenta que tenemos un comportamiento diferente si asignamos la salida de map de la siguiente manera (según la respuesta del gato Plus Plus')

f=(lambda pair: pair[0].append(pair[1])) 
a = ([],1) 
x = [a] 
x[:] = map(f,x) 
print(x) #prints None 
print(a) # prints [1] 

Tenga en cuenta que en el primer ejemplo, simplemente llamados f(a), no a=f(a) . ¿Por qué necesitamos asignación cuando usamos map y no cuando trabajamos fuera de map?