2010-07-21 17 views
5

En PHP se puede crear una variable de referencia, de manera que dos variables con nombre pueden mirar el mismo valor:Crear una referencia a una variable (similar a "= &" de PHP)?

$a = 1; 
$b =& $a; 
echo $a; // 1 
echo $b; // 1 
$b = 2; 
echo $a; // 2 

Busco para lograr algo similar en Python. Específicamente, quiero crear una referencia a la propiedad de un objeto, por ejemplo:

class Foo(object): 
    @property 
    def bar(self): return some_calculated_value 

foo_instance = Foo() 
ref = foo_instance.bar 
# so that 'ref' is referencing the bar property on foo, calculated when used. 

¿Esto es posible?

+3

Si no usa propiedades, pero usa funciones de método, esto es trivial. De lo contrario, esto es, probablemente, simplemente un error que está por ocurrir. Crear un * alias * crea una ambigüedad intencionalmente difícil de entender y difícil de depurar. ¿Por qué hacer eso con usted o con la gente que intentará mantener esto? ¿Cuál es el caso de uso para una ambigüedad como esta? –

+0

¿Por qué querrías hacer eso? – NullUserException

+0

Simplificación: aliasing 'someproxyobject.aholdingvariableto.anothersubobject.property' to 'foo'. –

Respuesta

5

Hay algo de más magia que se puede hacer en Python (no es que lo recomendaría y se requerirá la excavación de su parte ;-), pero usando un cierre puede ser suficiente para sus necesidades:

get_x = lambda: foo_instance.bar 
get_x() # yahoo! 

Editar, para aquellos que quieran "soporte de actualización", es todo acerca de los cierres:

def wrap_prop (obj, pname): 
    def _access(*v): 
    if not len(v): 
     return getattr(obj, pname) 
    else 
     setattr(obj, pname, v[0]) 
    return _access 

class z (object): 
    pass 

access = wrap_prop(z(), "bar") 
access(20) 
access() # yahoo! \o/ 

Sin salirse de los límites de la normalidad (para mí :-) Python aceptado, esto también se podría escribir para devolver un objeto con la propiedad de reenvío/proxy, imagine:

access = wrap_prop(z(), "bar") 
access.val = 20 
access.val # yahoo \o/ 

Algunos enlaces interesantes:

+0

Pero no es configurable. – kennytm

+0

@KennyTM: tampoco es la propiedad dada en el ejemplo de digitala. – JAB

+0

@JAB: ¿OP dijo que su ejemplo de Python es correcto? Creo que lo que OP quiere es reproducir [el ejemplo de PHP] (http://www.ideone.com/VdfrW) con $ a convirtiéndose en una propiedad, no necesariamente de solo lectura. – kennytm

1

No, lo que quiere no es posible. Todos los nombres en Python son referencias, pero siempre son referencias a objetos, no referencias a variables. No puede tener un nombre local que, cuando se "usa", vuelva a evaluar qué fue lo que lo creó.

Lo que tiene tiene es un objeto que actúa como un proxy, delegando la mayoría de las operaciones en las operaciones en lo que hace referencia internamente. Sin embargo, esto no siempre es transparente, y hay algunas operaciones que no puedes realizar. También es frecuentemente inconveniente tener que lo que está utilizando para cambiar durante una operación. En general, generalmente es mejor no intentar esto.

En su lugar, en su caso en particular, se mantendrá en torno foo_instance en lugar de ref, y sólo tiene que utilizar foo_instance.bar cada vez que quería a "utilizar" la referencia ref. Para situaciones más complicadas, o para una mayor abstracción, podría tener un tipo separado con una propiedad que hiciera exactamente lo que quería, o una función (generalmente un cierre) que supiera qué obtener y dónde.

1

Como han señalado otros, para hacer esto, necesitará mantener una referencia al objeto principal. Sin poner en duda sus razones para querer hacer esto, he aquí una solución posible:

class PropertyRef: 
    def __init__(self, obj, prop_name): 
     klass = obj.__class__ 
     prop = getattr(klass, prop_name) 
     if not hasattr(prop, 'fget'): 
      raise TypeError('%s is not a property of %r' % (prop_name, klass)) 
     self.get = lambda: prop.fget(obj) 
     if getattr(prop, 'fset'): 
      self.set = lambda value: prop.fset(obj, value)) 

class Foo(object): 
    @property 
    def bar(self): 
     return some_calculated_value 

>>> foo_instance = Foo() 
>>> ref = PropertyRef(foo_instance, 'bar') 
>>> ref.get() # Returns some_calculated_value 
>>> ref.set(some_other_value) 
>>> ref.get() # Returns some_other_value 

Por cierto, en el ejemplo que diste, la propiedad es de sólo lectura (no tiene un setter), por lo que no lo haría ser capaz de configurarlo de todos modos.

Si esto se siente como una cantidad innecesaria de código, es probable que sea - estoy casi seguro de que puede encontrar una mejor solución para su caso de uso, sea lo que sea.

Cuestiones relacionadas