Создать ссылку на переменную (похоже на PHP "= &")? - PullRequest
5 голосов
/ 21 июля 2010

В PHP можно создать ссылочную переменную, чтобы две именованные переменные могли смотреть на одно и то же значение:

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

Я хочу добиться чего-то похожего в Python. В частности, я хочу создать ссылку на свойство объекта, например:

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.

Возможно ли это?

Ответы [ 3 ]

5 голосов
/ 21 июля 2010

Есть еще немного магии, которая может быть сделана в Python (не то, чтобы я рекомендовал это, и это потребует копать с вашей стороны ;-), но использование закрытия может быть достаточным для ваших нужд:

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

Edit, для тех, кто хочет "поддержку обновлений", все дело в замыканиях:

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/

Не выходя за пределы обычно (для меня :-) принятого Python, это также можно записать вверните объект со свойством переадресации / прокси, представьте:

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

Некоторые интересные ссылки:

1 голос
/ 21 июля 2010

Как уже отмечали другие, для того, чтобы сделать это, вам нужно будет хранить ссылку на родительский объект.Не ставя под сомнение ваши причины желания сделать это, вот возможное решение:

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

Кстати, в приведенном вами примере свойство доступно только для чтения (не имеет установщика), так что вы быНе могу установить его в любом случае.

Если это похоже на ненужный объем кода, вероятно, это так - я почти уверен, что вы можете найти лучшее решение для своего варианта использования, каким бы оно ни было.

1 голос
/ 21 июля 2010

Нет, то, что вы хотите, невозможно.Все имена в Python являются ссылками, но они всегда ссылаются на объекты , а не на переменные .У вас не может быть локального имени, которое, когда оно «используется», переоценивает то, что было создано, оно создало.делегирование большинства операций над ним операциям, на которые он ссылается внутри.Тем не менее, это не всегда прозрачно, и есть некоторые операции, которые вы не можете прокси.Также часто бывает неудобно, если вы изменяете значение во время операции .В общем, обычно лучше не пробовать это.

Вместо этого, в вашем конкретном случае, вы должны оставить около foo_instance вместо ref и просто использовать foo_instance.bar всякий раз, когда захотите «использовать» ссылку ref.Для более сложных ситуаций или для большей абстракции у вас может быть отдельный тип со свойством, которое делает то, что вам нужно, или функция (обычно замыкание), которая знает, что и где получить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...