【问题标题】:How do I pass a variable by reference?如何通过引用传递变量?
【发布时间】:2010-11-02 10:17:18
【问题描述】:

Python 文档似乎不清楚参数是按引用传递还是按值传递,以下代码生成未更改的值“Original”

class PassByReference:
    def __init__(self):
        self.variable = 'Original'
        self.change(self.variable)
        print(self.variable)

    def change(self, var):
        var = 'Changed'

我可以做些什么来通过实际引用传递变量吗?

【问题讨论】:

标签: python reference parameter-passing pass-by-reference


【解决方案1】:

我解决了一个类似的要求如下:

要实现一个改变变量的成员函数,不要传递变量本身,而是传递一个functools.partial,其中包含引用该变量的setattr。 在change() 中调用functools.partial 将执行settatr 并更改实际引用的变量。

注意setattr需要变量名作为字符串。

class PassByReference(object):
    def __init__(self):
        self.variable = "Original"
        print(self.variable)        
        self.change(partial(setattr,self,"variable"))
        print(self.variable)

    def change(self, setter):
        setter("Changed")

【讨论】:

    【解决方案2】:

    dataclasses 呢?此外,它还允许您应用类型限制(也称为“类型提示”)。

    from dataclasses import dataclass
    
    @dataclass
    class Holder:
        obj: your_type # Need any type? Use "obj: object" then.
    
    def foo(ref: Holder):
        ref.obj = do_something()
    

    我同意人们的观点,在大多数情况下你最好考虑不使用它。

    然而,当我们谈论 contexts 时,值得这样了解。

    你可以设计明确的上下文类。在进行原型设计时,我更喜欢数据类,只是因为它们很容易来回序列化。

    干杯!

    【讨论】:

      【解决方案3】:

      大多数时候,通过引用传递的变量是类成员。 我建议的解决方案是使用装饰器来添加可变字段和相应属性。该字段是变量的类包装器。

      @refproperty 添加了self._myvar(可变)和self.myvar 属性。

      @refproperty('myvar')
      class T():
          pass
      
      def f(x):
         x.value=6
      
      y=T()
      y.myvar=3
      f(y._myvar)
      print(y.myvar) 
      

      它将打印 6。

      比较一下:

      class X:
         pass
      
      x=X()
      x.myvar=4
      
      def f(y):
          y=6
      
      f(x.myvar)
      print(x.myvar) 
      

      在这种情况下,它不起作用。它将打印 4。

      代码如下:

      def refproperty(var,value=None):
          def getp(self):
              return getattr(self,'_'+var).get(self)
      
          def setp(self,v):
              return getattr(self,'_'+var).set(self,v)
      
          def decorator(klass):
              orginit=klass.__init__
              setattr(klass,var,property(getp,setp))
      
              def newinit(self,*args,**kw):
                  rv=RefVar(value)
                  setattr(self,'_'+var,rv)
                  orginit(self,*args,**kw)
      
              klass.__init__=newinit
              return klass
          return decorator
      
      class RefVar(object):
          def __init__(self, value=None):
              self.value = value
          def get(self,*args):
              return self.value
          def set(self,main, value):
              self.value = value
      

      【讨论】:

        【解决方案4】:

        简单回答:

        在像 c++ 这样的 python 中,当您创建对象实例并将其作为参数传递时,不会生成实例本身的副本,因此您从函数外部和内部引用相同的实例并且能够修改组件同一对象实例的基准,因此变化对外部可见。

        对于基本类型,python 和 c++ 的行为也彼此相同,因为现在制作了实例的副本,因此外部看到/修改了与函数内部不同的实例。因此,内部的变化在外部是不可见的。

        python 和 c++ 的真正区别来了:

        c++有地址指针的概念,c++允许你改为传递指针,这样就绕过了基本类型的复制,这样函数内部就可以影响到和外部一样的实例,这样的变化也是对外可见。这在 python 中没有等价物,因此如果没有变通方法(例如创建包装器类型)是不可能的。

        这样的指针在python中可能很有用,但不像在c++中那么必要,因为在c++中,你只能返回一个实体,而在python中你可以返回多个用逗号分隔的值(即一个元组) .所以在 python 中,如果你有变量 a、b 和 c,并且想要一个函数来持久地修改它们(相对于外部),你可以这样做:

        a=4
        b=3
        c=8
        
        a,b,c=somefunc(a,b,c)
        # a,b,c now have different values here
        

        这样的语法在 c++ 中不容易实现,因此在 c++ 中你可以这样做:

        int a=4
        int b=3
        int c=8
        somefunc(&a,&b,&c)
        // a,b,c now have different values here
        

        【讨论】:

        • “对于基本类型,python 和 c++ 的行为也彼此相同,因为现在制作了实例的副本”,不,这是不正确的。对象的副本永远不会作为评估策略的一部分
        • 您的评论毫无意义。你说的是python还是c++?如果是python,那么无论它们是否是基本类型,解释器的工作方式都会有所不同。在内部,python 使用等效的堆栈来管理本地基本对象类型,这些是副本。如果不是,那么函数调用之外的原件将被不可逆转地修改,它们不是,这就提出了一个问题,你曾经使用过 python 吗?!
        • 我说的是 Python。解释器不区分“基本类型”和“其他类型”至少在 CPython 中,所有对象都分配在私有管理的堆上。您似乎对 Python 的内部原理一无所知,更不用说语言的语义了
        • 对象没有被修改通过分配给参数,但对所有对象都这样,因为这不是问题对象的类型,而是赋值的语义和评估策略。在 Python 中,某些类型只是不可变的。也就是说,它们没有公开修改器方法。但是,我们可以(如果我们愿意深入研究实现细节)对它们进行变异,实际上,您会看到评估策略完全相同
        • 这与对象无关!这是关于基本类型的。 Python 在幕后将它们视为对象,它们必须存在于内存中的某个位置。但是当作为参数传递给函数时,必须进行复制(根据信息论的基本规则),否则当函数返回时,传递给函数的原始变量也会被修改。但他们没有。尝试一下(请原谅大写,我不知道如何实现粗体效果)。他们唯一的办法就是这种情况,如果在内存中的其他地方有一个 COPY,那就是上面的内容。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-06-05
        相关资源
        最近更新 更多