2010-06-24 49 views
72

sé Python no tiene punteros, pero ¿hay una manera de tener este rendimiento 2 lugarPunteros en Python?

>>> a = 1 
>>> b = a # modify this line somehow so that b "points to" a 
>>> a = 2 
>>> b 
1 

?


He aquí un ejemplo: Quiero form.data['field']form.field.value y tener siempre el mismo valor. No es completamente necesario, pero creo que sería bueno.

+17

Tal vez pueda hacer una pregunta similar a S. Lott de (pero más productivo): nos muestras algún código real en el que quieres hacer esto? ¿Quizás incluso en otro idioma que sea más de tu gusto? Es probable que el problema que está tratando de resolver se preste a una solución más Pythonic, y centrarse en "Quiero indicadores" está oscureciendo la respuesta real. –

+6

No requiere mucha imaginación; Puedo pensar en docenas de razones para querer hacer esto. Simplemente no es cómo se hace en lenguajes sin puntero como Python; necesitas envolverlo en un contenedor que no sea invariable, como en la respuesta de Matt. –

+0

@Ned: Q actualizada. Otro ejemplo sería para una función, 'swap (a, b)'. No es que lo necesite en este momento. – mpen

Respuesta

34

quiero form.data['field'] y form.field.value tener siempre el mismo valor

Esto es factible, porque se trata de nombres representativos e indexación - es decir,, completamente construcciones diferentes de nombres desnudosa y b que está preguntando acerca de, y para con su solicitud es completamente imposible. ¿Por qué pedir algo imposible y totalmente diferente de lo (posible) que realmente quiere?!

Tal vez no se da cuenta de cuán drásticamente diferentes nombres desnudos y nombres decorados son. Cuando te refieres a un nombre simple a, obtienes exactamente el objeto por el que a estuvo vinculado por última vez en este ámbito (o una excepción si no estaba vinculado en este ámbito): este es un aspecto tan profundo y fundamental de Python que no puede ser subvertido. Cuando se refiere a un decorado nombre x.y, está pidiendo un objeto (se refiere al objeto x) para proporcionar "el atributo y" - y en respuesta a esa solicitud, el objeto puede realizar cálculos totalmente arbitrarios (y la indexación es bastante similar: también permite realizar cálculos arbitrarios en respuesta).

Ahora, su ejemplo de "real desapego" es misterioso porque en cada caso se involucran dos niveles de indexación o de obtención de atributos, por lo que la sutileza que anhela podría introducirse de muchas maneras. ¿Qué otros atributos se supone que tiene form.field tener, por ejemplo, además de value? Sin que una mayor .value cálculos, posibilidades incluirían:

class Form(object): 
    ... 
    def __getattr__(self, name): 
     return self.data[name] 

y

class Form(object): 
    ... 
    @property 
    def data(self): 
     return self.__dict__ 

La presencia de .value sugiere recoger la primera forma, además de una envoltura inútil de tipo de:

class KouWrap(object): 
    def __init__(self, value): 
     self.value = value 

class Form(object): 
    ... 
    def __getattr__(self, name): 
     return KouWrap(self.data[name]) 

Si asignaciones tales form.field.value = 23 también se supone que establece t que la entrada en form.data, a continuación, la envoltura debe ser más compleja de hecho, y no tan inútil:

class MciWrap(object): 
    def __init__(self, data, k): 
     self._data = data 
     self._k = k 
    @property 
    def value(self): 
     return self._data[self._k] 
    @value.setter 
    def value(self, v) 
     self._data[self._k] = v 

class Form(object): 
    ... 
    def __getattr__(self, name): 
     return MciWrap(self.data, name) 

El último ejemplo es aproximadamente tan cerca como se pone, en Python, con el sentido de "un puntero", como parece querer, pero es crucial entender que tales sutilezas solo pueden funcionar con indexando y/o nombres decorados, nunca con nombres desnudos como originalmente lo solicitó!

+10

Lo pregunté de la manera en que lo hice porque esperaba obtener una solución general que funcionara para todo, hasta "nombres desnudos" y no tenía un caso particular en mente en ese momento, solo que me había topado con este problema con la iteración anterior de este proyecto y no quería volver a encontrarme. De todos modos, esta es una gran respuesta! La incredulidad, sin embargo, es menos apreciada. – mpen

+2

@Mark, mira los comentarios en tu Q y verás que la "incredulidad" es una reacción generalizada, y A votó "no lo hagas, supera" seguido de uno eso dice "así es como es". Si bien es posible que no "aprecies" a las personas conocedoras de Python que reaccionan con asombro ante tus especificaciones originales, son bastante asombrosas en sí mismas ;-). –

+14

Sí, pero pareces asombrado por mi falta de conocimiento de Python ... como si fuera una especie extranjera: P – mpen

31

No hay forma de que pueda hacerlo cambiando solo esa línea. Que puede hacer:

a = [1] 
b = a 
a[0] = 2 
b[0] 

que crea una lista, asigna la referencia a una, entonces b También, utiliza la referencia para establecer el primer elemento a 2, luego accede a través de la variable b referencia.

+7

Ese es exactamente el tipo de inconsistencia que odio sobre Python y estos lenguajes dinámicos. (Sí, sí, no es realmente "inconsistente" porque está cambiando un atributo en lugar de la referencia, pero todavía no me gusta) – mpen

+3

@Mark: de hecho. Conozco a innumerables (bueno, algunas) personas que pasaron posiblemente horas buscando un "error" en su código y luego descubriendo que fue causado por una lista que no se copió por completo. – houbysoft

+10

No hay inconsistencia. Y no tiene nada que ver con el gran debate dinámico vs. dinámico. Si fueran dos referencias al mismo Java ArrayList, sería la misma sintaxis de módulo. Si usa objetos inmutables (como tuplas), no tiene que preocuparse por el objeto que se está cambiando a través de otra referencia. –

27

No es un insecto, él es una característica :-)

Cuando nos fijamos en el operador '=' en Python, no pensar en términos de asignación. No asignas cosas, las atas. = es un operador vinculante

Por lo tanto, en su código, le está dando al valor 1 un nombre: a. Entonces, está dando el valor en 'a' un nombre: b. Entonces está vinculando el valor 2 con el nombre 'a'. El valor ligado a b no cambia en esta operación.

Procedente de C-like, esto puede ser confuso, pero una vez que te acostumbras, encuentras que te ayuda a leer y razonar sobre tu código más claramente: el valor que tiene el nombre 'b' no cambia a menos que lo cambies explícitamente. Y si realiza una 'importación', encontrará que el Zen de Python establece que Explícito es mejor que implícito.

Tenga en cuenta también que los lenguajes funcionales como Haskell también usan este paradigma, con un gran valor en términos de robustez.

+26

Sabes, he leído respuestas como esta docenas de veces, y nunca lo he entendido. El comportamiento de 'a = 1; b = a; a = 2; 'es exactamente igual en Python, C y Java: b es 1. ¿Por qué este foco en" = no es una asignación, es vinculante "? –

+2

Usted asigna cosas. Es por eso que se llama una [declaración de asignación] (http://docs.python.org/reference/simple_stmts.html). La distinción que estás diciendo no tiene sentido. Y esto no tiene nada que ver con compiled v. Interpreted o static v. Dynamic. Java es un lenguaje compilado con comprobación de tipos estática y tampoco tiene punteros. –

+3

¿Qué hay de C++? "b" podría ser una referencia a "a". Comprender la diferencia entre la asignación y el enlace es fundamental para comprender por completo por qué Mark no puede hacer lo que le gustaría hacer y cómo se diseñan los lenguajes como Python. Conceptualmente (no necesariamente en implementación), "a = 1" no sobrescribe el bloque de memoria llamado "a" con 1; asigna un nombre "a" al objeto "1" ya existente, que es fundamentalmente diferente de lo que sucede en C. Es por eso que los punteros como concepto no pueden existir en Python: se volverían obsoletos la próxima vez que la variable original fue "asignada sobre". –

11

Desde un punto de vista, todo es un puntero en Python. Su ejemplo funciona de manera muy parecida al código C++.

int* a = new int(1); 
int* b = a; 
a = new int(2); 
cout << *b << endl; // prints 1 

(Un equivalente más cercano sería utilizar algún tipo de shared_ptr<Object> en lugar de int*.)

He aquí un ejemplo: Quiero form.data [ 'campo'] y form.field.value tener siempre el mismo valor . No es completamente necesario, pero creo que sería agradable.

Puede hacerlo mediante la sobrecarga de __getitem__ en la clase form.data.

+0

'form.data' no es una clase. ¿Es necesario hacerlo uno o puedo anularlo sobre la marcha? (Es solo un dict de Python) Además, los datos tendrían que tener una referencia de regreso a 'form' para acceder a los campos ... lo que hace que la implementación sea fea. – mpen

13

Sí! hay una forma de usar una variable como un puntero en python!

Lamento decir que muchas de las respuestas fueron parcialmente erróneas. En principio, cada asignación igual (=) comparte la dirección de la memoria (verifique la función id (obj)), pero en la práctica no es así. Hay variables cuyo comportamiento igual ("=") funciona en el último término como una copia del espacio de memoria, principalmente en objetos simples (por ejemplo, "int" objeto) y otros en los que no (por ejemplo, "lista", "dict" objetos) .

Aquí es un ejemplo de asignación de puntero

dict1 = {'first':'hello', 'second':'world'} 
dict2 = dict1 # pointer assignation mechanism 
dict2['first'] = 'bye' 
dict1 
>>> {'first':'bye', 'second':'world'} 

Aquí es un ejemplo de copia de asignación

a = 1 
b = a # copy of memory mechanism. up to here id(a) == id(b) 
b = 2 # new address generation. therefore without pointer behaviour 
a 
>>> 1 

puntero asignación es una herramienta muy útil para aliasing sin la pérdida de memoria adicional, en cierta situaciones para realizar código cómodo,

class cls_X(): 
    ... 
    def method_1(): 
     pd1 = self.obj_clsY.dict_vars_for_clsX['meth1'] # pointer dict 1: aliasing 
     pd1['var4'] = self.method2(pd1['var1'], pd1['var2'], pd1['var3']) 
    #enddef method_1 
    ... 
#endclass cls_X 

pero uno hav e tener en cuenta este uso para evitar errores de código.

Para finalizar, de forma predeterminada algunas variables son nombres desnudos (objetos simples como int, float, str, ...), y algunos son punteros cuando se asignan entre ellos (por ejemplo, dict1 = dict2). Cómo reconocerlos? solo prueba este experimento con ellos. En los IDE con panel de explorador variable, generalmente aparece la dirección de memoria ("@axbbbbbb ...") en la definición de objetos de mecanismo de puntero.

Sugiero investigar en el tema. Hay muchas personas que saben mucho más sobre este tema. (ver módulo "ctypes") Espero que sea útil. ¡Disfruta el buen uso de los objetos! Saludos, José Crespo

2
id(1) 
1923344848 # identity of the location in my memory  
>> a = 1 
>> b = a # or equivalently b = 1, because 1 is immutable 
>> id(a) 
1923344848 
>> id(b) 
1923344848 

Como se puede ver a y b son sólo nombres que hacen referencia al mismo objeto 1. Si más adelante se escribe a = 2, reasigna el nombre a a un objeto diferente 2, pero no el b que continuará haciendo referencia a 1:

>> id(2) 
1923344880 
>> a = 2 
>> id(a) 
1923344880 # same as id(2) 
>> id(b) 
1923344848 # same as id(1) 

¿Qué sucederá si tuviera un objeto mutable?

>> id([1]) 
328817608 
>> id([1]) 
328664968 # different 
>> a = [1] 
>> id(a) 
328817800 
>> id(a) 
328817800 # same as before 
>> b = a # not equivalent to b = [1] 
>> id(b) 
328817800 # same as id(a) 

Ahora, se hace referencia a que el mismo objeto de la lista con los nombres y ab. Puede mutar esta lista, pero se mantendrá el mismo objeto, y a y b a los dos para continuar haciendo referencia a ella

>> a[0] = 2 
>> a 
[2] 
>> b 
[2] 
>> id(a) 
328817800 # same as before 
>> id(b) 
328817800 # same as before