如何创建变量的引用(类似PHP的“=&”)?

5 投票
3 回答
4020 浏览
提问于 2025-04-16 01:38

在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 个回答

1

不,这个想法是行不通的。在Python中,所有的名字其实都是指向某个东西的引用,但它们指向的是对象,而不是变量。你不能有一个局部名字,当你“使用”它的时候,它会重新评估当初是怎么创建的。

不过,你可以有一个对象,它充当一个代理,负责把大部分操作转发给它内部引用的对象。但是,这种方式并不总是透明的,有些操作是无法通过代理来完成的。而且,在操作过程中,如果你代理的对象发生了变化,这通常会让事情变得很麻烦。总的来说,通常不建议这样做。

相反,在你的具体情况下,你可以保留foo_instance,而不是ref,每当你想“使用”ref时,就直接用foo_instance.bar。如果情况更复杂,或者你需要更高层次的抽象,你可以创建一个单独的类型,里面有一个属性,正好实现你想要的功能,或者写一个函数(通常是闭包),它知道该获取什么和在哪里获取。

1

正如其他人提到的,要做到这一点,你需要保留对父对象的引用。虽然我不想问你为什么想这样做,但这里有一个可能的解决方案:

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

顺便说一下,在你给的例子中,这个属性是只读的(没有设置器),所以你根本无法去设置它。

如果你觉得这段代码写得有点多,那可能确实是这样——我几乎可以肯定你能找到更好的解决方案,适合你的具体情况,无论是什么。

5

在Python中还有一些很有趣的用法(虽然我不太推荐这样做,而且你可能需要自己多研究一下 ;-),但使用闭包可能就能满足你的需求:

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

如果你想要“更新支持”,那么关键就在于闭包:

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/

这里有一些有趣的链接:

撰写回答