如何通过引用传递变量?

时间:2009-06-12 10:23:52

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

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

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

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

我可以通过实际参考传递变量吗?

32 个答案:

答案 0 :(得分:2509)

参数为passed by assignment。这背后的理由是双重的:

  1. 传入的参数实际上是对象的引用(但引用按值传递)
  2. 某些数据类型是可变的,但其他数据类型不是
  3. 所以:

    • 如果你将一个 mutable 对象传递给一个方法,那么该方法会获得对同一个对象的引用,你可以将它改变为你心中的喜悦,但如果你重新引用它方法,外部范围将不知道它,并在完成后,外部引用仍将指向原始对象。

    • 如果将 immutable 对象传递给方法,则仍然无法重新绑定外部引用,甚至无法改变对象。

    为了更清楚,让我们举一些例子。

    列表 - 可变类型

    让我们尝试修改传递给方法的列表:

    def try_to_change_list_contents(the_list):
        print('got', the_list)
        the_list.append('four')
        print('changed to', the_list)
    
    outer_list = ['one', 'two', 'three']
    
    print('before, outer_list =', outer_list)
    try_to_change_list_contents(outer_list)
    print('after, outer_list =', outer_list)
    

    输出:

    before, outer_list = ['one', 'two', 'three']
    got ['one', 'two', 'three']
    changed to ['one', 'two', 'three', 'four']
    after, outer_list = ['one', 'two', 'three', 'four']
    

    由于传入的参数是对outer_list的引用,而不是它的副本,我们可以使用变异列表方法来更改它,并将更改反映在外部范围内。

    现在让我们看看当我们尝试更改作为参数传入的引用时会发生什么:

    def try_to_change_list_reference(the_list):
        print('got', the_list)
        the_list = ['and', 'we', 'can', 'not', 'lie']
        print('set to', the_list)
    
    outer_list = ['we', 'like', 'proper', 'English']
    
    print('before, outer_list =', outer_list)
    try_to_change_list_reference(outer_list)
    print('after, outer_list =', outer_list)
    

    输出:

    before, outer_list = ['we', 'like', 'proper', 'English']
    got ['we', 'like', 'proper', 'English']
    set to ['and', 'we', 'can', 'not', 'lie']
    after, outer_list = ['we', 'like', 'proper', 'English']
    

    由于the_list参数是按值传递的,因此为其分配新列表不会影响方法外部的代码。 the_listouter_list引用的副本,我们the_list指向新列表,但无法更改outer_list指向的位置。

    字符串 - 不可变类型

    它是不可变的,所以我们无法改变字符串的内容

    现在,让我们尝试更改参考

    def try_to_change_string_reference(the_string):
        print('got', the_string)
        the_string = 'In a kingdom by the sea'
        print('set to', the_string)
    
    outer_string = 'It was many and many a year ago'
    
    print('before, outer_string =', outer_string)
    try_to_change_string_reference(outer_string)
    print('after, outer_string =', outer_string)
    

    输出:

    before, outer_string = It was many and many a year ago
    got It was many and many a year ago
    set to In a kingdom by the sea
    after, outer_string = It was many and many a year ago
    

    同样,由于the_string参数是按值传递的,因此为其分配新字符串不会影响方法外部的代码。 the_stringouter_string引用的副本,我们the_string指向一个新字符串,但无法更改outer_string指向的位置。

    我希望这可以解决一些问题。

    编辑:有人注意到,这并没有回答@David最初提出的问题,“我能做些什么来通过实际参考传递变量?”。让我们继续努力。

    我们如何解决这个问题?

    正如@ Andrea的回答所示,您可以返回新值。这不会改变传入方式,但可以让您获得想要的信息:

    def return_a_whole_new_string(the_string):
        new_string = something_to_do_with_the_old_string(the_string)
        return new_string
    
    # then you could call it like
    my_string = return_a_whole_new_string(my_string)
    

    如果你真的想避免使用返回值,你可以创建一个类来保存你的值并将其传递给函数或使用现有的类,如列表:

    def use_a_wrapper_to_simulate_pass_by_reference(stuff_to_change):
        new_string = something_to_do_with_the_old_string(stuff_to_change[0])
        stuff_to_change[0] = new_string
    
    # then you could call it like
    wrapper = [my_string]
    use_a_wrapper_to_simulate_pass_by_reference(wrapper)
    
    do_something_with(wrapper[0])
    

    虽然这看起来有点麻烦。

答案 1 :(得分:605)

问题来自对Python中变量的误解。如果您已经习惯了大多数传统语言,那么您将拥有以下序列中发生的事情的心理模型:

a = 1
a = 2

您认为a是存储值1的内存位置,然后更新为存储值2。这不是Python中的工作方式。相反,a作为对具有值1的对象的引用而开始,然后被重新分配为对具有值2的对象的引用。即使a不再引用第一个对象,这两个对象可能会继续共存;实际上,它们可能会被程序中的任何其他引用共享。

当您使用参数调用函数时,会创建一个引用传入的对象的新引用。这与函数调用中使用的引用是分开的,因此无法更新该引用并使其成为可能引用一个新对象。在您的示例中:

def __init__(self):
    self.variable = 'Original'
    self.Change(self.variable)

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

self.variable是对字符串对象'Original'的引用。当您致电Change时,您会为该对象创建第二个引用var。在函数内部,您将引用var重新分配给不同的字符串对象'Changed',但引用self.variable是独立的,不会更改。

解决这个问题的唯一方法是传递一个可变对象。因为两个引用都引用同一个对象,所以对象的任何更改都会反映在两个位置。

def __init__(self):         
    self.variable = ['Original']
    self.Change(self.variable)

def Change(self, var):
    var[0] = 'Changed'

答案 2 :(得分:283)

我发现其他答案相当漫长而复杂,所以我创建了这个简单的图来解释Python处理变量和参数的方式。 enter image description here

答案 3 :(得分:227)

它既不是按值传递,也不是按引用传递 - 它是逐个调用的。请见Fredrik Lundh:

http://effbot.org/zone/call-by-object.htm

这是一个重要的引用:

  

“...变量[名称]是不是对象;它们不能用其他变量表示或由对象引用。”

在您的示例中,当调用Change方法时,会为其创建namespace;并且var成为字符串对象'Original'在该命名空间内的名称。然后,该对象在两个名称空间中具有名称。接下来,var = 'Changed'var绑定到一个新的字符串对象,因此该方法的命名空间会忘记'Original'。最后,忘记了该命名空间,并将字符串'Changed'与它一起。

答案 4 :(得分:156)

考虑通过分配而不是通过引用/按值传递的内容。这样,只要您了解正常分配期间发生的情况,就会清楚地知道发生了什么。

因此,将列表传递给函数/方法时,会将列表分配给参数名称。附加到列表将导致列表被修改。重新分配里面的函数将不会更改原始列表,因为:

a = [1, 2, 3]
b = a
b.append(4)
b = ['a', 'b']
print a, b      # prints [1, 2, 3, 4] ['a', 'b']

由于无法修改不可变类型,它们似乎就像通过值传递一样 - 将int传递给函数意味着将int赋给函数参数。您只能重新分配,但不会更改原始变量值。

答案 5 :(得分:59)

Effbot(又名Fredrik Lundh)将Python的变量传递样式描述为逐个调用:http://effbot.org/zone/call-by-object.htm

对象在堆上分配,指向它们的指针可以在任何地方传递。

  • 当您进行x = 1000之类的赋值时,会创建一个字典条目,将当前命名空间中的字符串“x”映射到指向包含一千个整数对象的指针。

  • 使用x = 2000更新“x”时,会创建一个新的整数对象,并更新字典以指向新对象。旧的一千个对象没有变化(可能存在也可能不存在,具体取决于是否有其他东西引用该对象)。

  • 当您执行y = x等新作业时,会创建一个新词典条目“y”,指向与“x”条目相同的对象。

  • 字符串和整数等对象是不可变的。这只是意味着没有方法可以在创建对象后更改对象。例如,一旦创建了整数对象一千,它就永远不会改变。通过创建新的整数对象来完成数学。

  • 列表之类的对象是 mutable 。这意味着可以通过指向对象的任何内容来更改对象的内容。例如,x = []; y = x; x.append(10); print y将打印[10]。空列表已创建。 “x”和“y”都指向同一个列表。 append 方法改变(更新)列表对象(如向数据库添加记录),结果对“x”和“y”都可见(正如数据库更新可见与该数据库的每个连接。)

希望能为您澄清问题。

答案 6 :(得分:58)

从技术上讲, Python总是使用传递参考值。我将重复my other answer以支持我的陈述。

Python始终使用传递引用值。没有任何例外。任何变量赋值都意味着复制参考值。没有例外。任何变量都是绑定到参考值的名称。总是

您可以将参考值视为目标对象的地址。使用时会自动取消引用该地址。这样,使用参考值,您似乎直接使用目标对象。但总会有一个参考,更多的是跳到目标。

以下示例证明Python使用通过引用传递:

Illustrated example of passing the argument

如果参数是按值传递的,则无法修改外部lst。绿色是目标对象(黑色是存储在内部的值,红色是对象类型),黄色是内部具有参考值的内存 - 绘制为箭头。蓝色实线箭头是传递给函数的参考值(通过虚线蓝色箭头路径)。丑陋的深黄色是内部字典。 (它实际上也可以画成绿色椭圆。颜色和形状只表示它是内部的。)

您可以使用id()内置函数来了解参考值(即目标对象的地址)。

在编译语言中,变量是一个能够捕获类型值的内存空间。在Python中,变量是绑定到引用变量的名称(内部捕获为字符串),该引用变量保存目标对象的引用值。变量的名称是内部字典中的键,该字典项的值部分将参考值存储到目标。

参考值隐藏在Python中。没有任何显式用户类型用于存储参考值。但是,您可以使用list元素(或任何其他合适的容器类型中的元素)作为引用变量,因为所有容器都将元素存储为对目标对象的引用。换句话说,元素实际上不包含在容器中 - 只有元素的引用是。

答案 7 :(得分:41)

我通常使用的一个简单技巧就是将其包装在一个列表中:

def Change(self, var):
    var[0] = 'Changed'

variable = ['Original']
self.Change(variable)      
print variable[0]

(是的,我知道这可能很不方便,但有时这样做很简单。)

答案 8 :(得分:37)

Python

中没有变量

理解参数传递的关键是停止思考“变量”。 Python中有名称和对象,它们在一起 看起来像变量,但始终区分三者是有用的。

  1. Python有名称和对象。
  2. 分配将名称绑定到对象。
  3. 将参数传递给函数还会将名称(函数的参数名称)绑定到对象。
  4. 这就是它的全部。可变性与这个问题无关。

    示例:

    a = 1
    

    这将名称a绑定到包含值1的整数类型的对象。

    b = x
    

    这会将名称b绑定到名称x当前绑定的同一对象。 之后,名称b不再与名称x无关。

    请参阅Python 3语言参考中的3.14.2部分。

    如何阅读问题中的示例

    在问题中显示的代码中,语句self.Change(self.variable)将名称var(在函数Change的范围内)绑定到包含值'Original'的对象赋值var = 'Changed'(在函数体Change中)再次指定相同的名称:对于其他一些对象(碰巧也包含一个字符串,但可能完全是其他东西)。

    如何通过引用传递

    (编辑2019-04-28)

    因此,如果您要更改的内容是一个可变对象,则没有问题,因为所有内容都通过引用有效传递。

    如果它是immutable对象(例如bool,number,string),那么可以将其包装在一个可变对象中。
    对此快速而肮脏的解决方案是一个单元素列表(而不是self.variable,传递[self.variable]并在函数修改var[0]中。) pythonic方法越多,引入一个简单的,一个属性的类。该函数接收类的实例并操纵属性。

答案 9 :(得分:36)

(编辑 - 布莱尔更新了他非常受欢迎的答案,以便现在准确无误)

我认为值得注意的是,目前投票率最高的帖子(布莱尔康拉德)虽然在结果方面是正确的,但却具有误导性,并且基于其定义是不正确的。虽然有许多语言(如C)允许用户通过引用传递或按值传递,但Python不是其中之一。

David Cournapeau的回答指出了真正的答案,并解释了为什么布莱尔康拉德的帖子中的行为似乎是正确的,而定义则没有。

如果Python按值传递,则所有语言都按值传递,因为必须发送某些数据(无论是“值”还是“引用”)。但是,这并不意味着Python在C程序员会想到它的意义上是通过值传递的。

如果你想要这种行为,布莱尔康拉德的回答很好。但是,如果你想知道为什么Python既没有通过价值传递也没有通过引用传递的细节,请阅读David Cournapeau的回答。

答案 10 :(得分:23)

你在这里得到了一些非常好的答案。

x = [ 2, 4, 4, 5, 5 ]
print x  # 2, 4, 4, 5, 5

def go( li ) :
  li = [ 5, 6, 7, 8 ]  # re-assigning what li POINTS TO, does not
  # change the value of the ORIGINAL variable x

go( x ) 
print x  # 2, 4, 4, 5, 5  [ STILL! ]


raw_input( 'press any key to continue' )

答案 11 :(得分:17)

在这种情况下,方法var中标题为Change的变量被分配了对self.variable的引用,您立即将字符串分配给var。它不再指向self.variable。以下代码段显示了修改varself.variable指向的数据结构时会发生什么情况,在本例中为列表:

>>> class PassByReference:
...     def __init__(self):
...         self.variable = ['Original']
...         self.change(self.variable)
...         print self.variable
...         
...     def change(self, var):
...         var.append('Changed')
... 
>>> q = PassByReference()
['Original', 'Changed']
>>> 

我相信其他人可以进一步澄清这一点。

答案 12 :(得分:16)

Python的pass-by-assignment方案与C ++的引用参数选项并不完全相同,但事实证明它与C语言(和其他语言)的参数传递模型非常相似:

  • 不可变参数实际上是通过“按值传递的。”整数和字符串等对象通过对象引用而不是通过复制传递,但是因为无论如何都无法更改不可变对象,效果就像制作副本一样。
  • 可变参数有效地传递“ by pointer 。”列表之类的对象 和字典也通过对象引用传递,这类似于C的方式 将数组作为指针传递 - 可变对象可以在函数中就地更改, 很像C阵列。

答案 13 :(得分:15)

正如你所说,你需要有一个可变对象,但是我建议你检查全局变量,因为它们可以帮助你甚至解决这类问题!

http://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

示例:

>>> def x(y):
...     global z
...     z = y
...

>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'z' is not defined

>>> x(2)
>>> x
<function x at 0x00000000020E1730>
>>> y
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'y' is not defined
>>> z
2

答案 14 :(得分:13)

这里的答案有很多见解,但我认为这里没有明确提到另外一点。引自python文档https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

&#34;在Python中,仅在函数内引用的变量是隐式全局变量。如果在函数体内的任何位置为变量分配了一个新值,则假定它是一个局部变量。如果在函数内部为变量赋予了新值,则该变量隐式为局部变量,您需要将其显式声明为“全局”。 虽然起初有点令人惊讶,但片刻的考虑解释了这一点。一方面,要求指定变量的全局性可以防止意外的副作用。另一方面,如果所有全局引用都需要全局,那么您将一直使用全局。您必须将对内置函数或导入模块的组件的每个引用声明为全局。这种混乱会破坏全球声明识别副作用的有用性。&#34;

即使将可变对象传递给函数,这仍然适用。对我来说,清楚地解释了分配给对象和操作函数中对象之间行为差异的原因。

def test(l):
    print "Received", l , id(l)
    l = [0, 0, 0]
    print "Changed to", l, id(l)  # New local object created, breaking link to global l

l= [1,2,3]
print "Original", l, id(l)
test(l)
print "After", l, id(l)

给出:

Original [1, 2, 3] 4454645632
Received [1, 2, 3] 4454645632
Changed to [0, 0, 0] 4474591928
After [1, 2, 3] 4454645632

对未声明为全局变量的全局变量的赋值因此会创建一个新的本地对象并断开与原始对象的链接。

答案 15 :(得分:9)

这是Python中使用的概念pass by object的简单(我希望)解释 无论何时将对象传递给函数,对象本身都会被传递(Python中的对象实际上是您在其他编程语言中称为值的对象),而不是对该对象的引用。换句话说,当你打电话:

def change_me(list):
   list = [1, 2, 3]

my_list = [0, 1]
change_me(my_list)

正在传递实际对象 - [0,1](在其他编程语言中称为值)。事实上,函数change_me将尝试执行以下操作:

[0, 1] = [1, 2, 3]

显然不会改变传递给函数的对象。如果函数看起来像这样:

def change_me(list):
   list.append(2)

然后调用将导致:

[0, 1].append(2)

这显然会改变对象。 This answer解释得很清楚。

答案 16 :(得分:8)

除了关于这些内容如何在Python中运行的所有重要解释之外,我还没有看到针对该问题的简单建议。当您似乎创建对象和实例时,处理实例变量并更改它们的pythonic方法如下:

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

    def Change(self):
        self.variable = 'Changed'

在实例方法中,您通常会引用self来访问实例属性。在__init__中设置实例属性并在实例方法中读取或更改它们是正常的。这也是您将第一个参数self传递给def Change

的原因

另一个解决方案是创建一个这样的静态方法:

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

    @staticmethod
    def Change(var):
        var = 'Changed'
        return var

答案 17 :(得分:6)

通过引用传递对象有一个小技巧,即使语言无法实现。它也适用于Java,它是带有一个项目的列表。 ; - )

verification_id

这是一个丑陋的黑客,但它确实有效。 ;-P

答案 18 :(得分:4)

我使用以下方法将一些Fortran代码快速转换为Python。是的,它并没有像原始问题那样通过引用传递,但在某些情况下是一个简单的解决方法。

a=0
b=0
c=0
def myfunc(a,b,c):
    a=1
    b=2
    c=3
    return a,b,c

a,b,c = myfunc(a,b,c)
print a,b,c

答案 19 :(得分:3)

虽然通过引用传递并不适合python并且应该很少使用,但是有一些解决方法实际上可以使得当前分配给局部变量的对象或甚至从被调用函数内部重新分配局部变量。

基本思想是拥有一个可以进行访问的函数,可以作为对象传递给其他函数或存储在类中。

一种方法是在包装函数中使用global(对于全局变量)或nonlocal(对于函数中的局部变量)。

def change(wrapper):
    wrapper(7)

x = 5
def setter(val):
    global x
    x = val
print(x)

同样的想法适用于阅读和del变量。

对于只是阅读,甚至有一种更简单的方法就是使用lambda: x返回一个可调用的,当被调用时返回x的当前值。这有点像&#34;呼叫名称&#34;在遥远的过去用于语言。

传递3个包装器来访问变量有点笨拙,因此可以将它们包装到具有代理属性的类中:

class ByRef:
    def __init__(self, r, w, d):
        self._read = r
        self._write = w
        self._delete = d
    def set(self, val):
        self._write(val)
    def get(self):
        return self._read()
    def remove(self):
        self._delete()
    wrapped = property(get, set, remove)

# left as an exercise for the reader: define set, get, remove as local functions using global / nonlocal
r = ByRef(get, set, remove)
r.wrapped = 15

蟒蛇&#34;反射&#34;支持可以获得一个能够在给定范围内重新分配名称/变量的对象,而无需在该范围内明确定义函数:

class ByRef:
    def __init__(self, locs, name):
        self._locs = locs
        self._name = name
    def set(self, val):
        self._locs[self._name] = val
    def get(self):
        return self._locs[self._name]
    def remove(self):
        del self._locs[self._name]
    wrapped = property(get, set, remove)

def change(x):
    x.wrapped = 7

def test_me():
    x = 6
    print(x)
    change(ByRef(locals(), "x"))
    print(x)

此处ByRef类包含字典访问权限。因此,wrapped的属性访问被转换为传递的字典中的项访问权限。通过传递内置locals的结果和局部变量的名称,最终访问局部变量。从3.5开始的python文档建议更改字典可能不起作用,但它似乎对我有用。

答案 20 :(得分:3)

考虑到python处理值和引用它们的方式,可以引用任意实例属性的唯一方法是按名称:

std::cout << "Debug stuff" << calculateThisAndThat() << "\n";

在实际代码中,您当然会在dict查找中添加错误检查。

答案 21 :(得分:2)

由于您的示例恰好是面向对象的,因此您可以进行以下更改以获得类似的结果:

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

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

# o.variable will equal 'Changed'
o = PassByReference()
assert o.variable == 'Changed'

答案 22 :(得分:2)

Python中的按引用传递与C ++ / Java中的按引用传递概念完全不同。

  • Java:所有内容均通过引用传递,因此调用者可以看到被调用函数中对参数所做的所有更改。
  • C ++:都允许按引用传递或按值传递。如果参数是通过引用传递的,则可以根据是否以const形式传递参数来对其进行修改。但是,无论是否为const,该参数都将保留对对象的引用,并且无法将引用分配为指向所调用函数内的另一个对象。
  • Python: Python是“按对象传递引用”,通常这样说:“对象引用按值传递。” [此处阅读] 1。调用者和函数都引用相同的对象,但是函数中的参数是一个新变量,它仅在调用者中保存对象的副本。像C ++一样,可以在函数中修改或不修改参数-这取决于传递的对象的类型。例如;不变的对象类型不能在调用的函数中修改,而可变的对象可以更新或重新初始化。更新或重新分配/重新初始化可变变量之间的关键区别在于,更新后的值会反映在被调用函数中,而重新初始化后的值则不会。将新对象分配给可变变量的范围是python中函数的局部范围。 @ blair-conrad提供的示例很好理解这一点。

答案 23 :(得分:1)

我是Python的新手,从昨天开始学习(尽管我已经有45年的编程经验了。)

我之所以来到这里,是因为我正在编写一个函数,希望有两个所谓的输出参数。如果只是一个外部参数,那么我现在不会挂断检查引用/值在Python中的工作方式。我只会使用该函数的返回值来代替。但是因为我需要两个这样的参数,所以我觉得需要对其进行梳理。

在这篇文章中,我将展示如何解决我的情况。也许其他来这里的人可能会发现它很有价值,即使它并不能完全解决主题问题。经验丰富的Python程序员当然已经知道我使用的解决方案,但这对我来说是新的。

从这里的答案中,我可以很快看到Python在这方面的工作方式与Javascript有点类似,并且如果需要参考功能,则需要使用变通方法。

但是后来我发现在Python中没有以前在其他语言中见过的东西,这就是您可以用简单的逗号分隔的方式从函数中返回多个值,如下所示:

def somefunction(p):
    a=p+1
    b=p+2
    c=-p
    return a, b, c

,并且您可以类似地在呼叫方进行处理

x, y, z = somefunction(w)

那对我来说足够好了,我很满意。无需使用某些解决方法。

当然,在其他语言中,您也可以返回许多值,但是通常是在对象的from中,因此您需要相应地调整调用方。

Python的实现方式既简单又好。

如果您想通过引用进一步模仿 ,则可以执行以下操作:

def somefunction(a, b, c):
    a = a * 2
    b = b + a
    c = a * b * c
    return a, b, c

x = 3
y = 5
z = 10
print(F"Before : {x}, {y}, {z}")

x, y, z = somefunction(x, y, z)

print(F"After  : {x}, {y}, {z}")

给出此结果

Before : 3, 5, 10  
After  : 6, 11, 660  

答案 24 :(得分:1)

或者你可以使用 ctypes 女巫看起来像这样

import ctypes

def f(a):
    a.value=2398 ## resign the value in a function

a = ctypes.c_int(0)
print("pre f", a)
f(a)
print("post f", a)

as a 是一个 c int 而不是 python 整数,并且显然是通过引用传递的。但是你必须小心,因为可能会发生奇怪的事情,因此不建议

答案 25 :(得分:0)

您只能使用空类作为存储引用对象的实例,因为内部对象属性存储在实例字典中。参见示例。

class RefsObj(object):
    "A class which helps to create references to variables."
    pass

...

# an example of usage
def change_ref_var(ref_obj):
    ref_obj.val = 24

ref_obj = RefsObj()
ref_obj.val = 1
print(ref_obj.val) # or print ref_obj.val for python2
change_ref_var(ref_obj)
print(ref_obj.val)

答案 26 :(得分:0)

由于字典是通过引用传递的,因此您可以使用dict变量在其中存储任何引用的值。

# returns the result of adding numbers `a` and `b`
def AddNumbers(a, b, ref): # using a dict for reference
    result = a + b
    ref['multi'] = a * b # reference the multi. ref['multi'] is number
    ref['msg'] = "The result: " + str(result) + " was nice!" # reference any string (errors, e.t.c). ref['msg'] is string
    return result # return the sum

number1 = 5
number2 = 10
ref = {} # init a dict like that so it can save all the referenced values. this is because all dictionaries are passed by reference, while strings and numbers do not.

sum = AddNumbers(number1, number2, ref)
print("sum: ", sum)             # the return value
print("multi: ", ref['multi'])  # a referenced value
print("msg: ", ref['msg'])      # a referenced value

答案 27 :(得分:0)

因为似乎没有地方提到一种模拟引用的方法,例如从C ++将使用“更新”函数并传递该函数,而不是传递实际变量(或更确切地说,是“名称”):

def need_to_modify(update):
    update(42) # set new value 42
    # other code

def call_it():
    value = 21
    def update_value(new_value):
        nonlocal value
        value = new_value
    need_to_modify(update_value)
    print(value) # prints 42

这对于“仅引用”或在具有多个线程/进程的情况下(通过使更新功能线程/多处理安全)最为有用。

显然,以上内容不允许读取值,而只能对其进行更新。

答案 28 :(得分:0)

很可能不是最可靠的方法,但这是有效的,请记住,您正在重载内置 str 函数,这通常是您不想做的事情:

import builtins

class sstr(str):
    def __str__(self):
        if hasattr(self, 'changed'):
            return self.changed

        return self

    def change(self, value):
        self.changed = value

builtins.str = sstr

def change_the_value(val):
    val.change('After')

val = str('Before')
print (val)
change_the_value(val)
print (val)

答案 29 :(得分:0)

这可能是一个优雅的面向对象的解决方案,而 Python 中没有此功能。一个更优雅的解决方案是让您从中创建子类的任何类。或者您可以将其命名为“MasterClass”。但是,与其只有一个变量和一个布尔值,不如让它们成为某种集合。我修复了实例变量的命名以符合 PEP 8。

class PassByReference:
    def __init__(self, variable, pass_by_reference=True):
        self._variable_original = 'Original'
        self._variable = variable
        self._pass_by_reference = pass_by_reference # False => pass_by_value
        self.change(self.variable)
        print(self)

    def __str__(self):
        print(self.get_variable())

    def get_variable(self):
        if pass_by_reference == True:
            return self._variable
        else:
            return self._variable_original

    def set_variable(self, something):
        self._variable = something

    def change(self, var):
        self.set_variable(var)

def caller_method():

    pbr = PassByReference(variable='Changed') # this will print 'Changed'
    variable = pbr.get_variable() # this will assign value 'Changed'

    pbr2 = PassByReference(variable='Changed', pass_by_reference=False) # this will print 'Original'
    variable2 = pbr2.get_variable() # this will assign value 'Original'
    

答案 30 :(得分:0)

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

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

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

setattr

答案 31 :(得分:0)

dataclasses 怎么样?此外,它还允许您应用类型限制(又名“类型提示”)。

Sub Testing()

Dim objRequest As Object
Dim strUrl As String
Dim blnAsync As Boolean
Dim strResponse As String
Dim idno As Variant
Dim ws As Worksheet
    
    Set ws = Sheet4
    
    

    LRow = ws.Range("B" & ws.Rows.Count).End(xlUp).Row
    
    For r = LRow To 2 Step -1
    idno = ws.Cells(r, "P").Value
                If ws.Cells(r, "B") <> "" And ws.Cells(r, "L") = "Tenu" Then

                Set objRequest = CreateObject("MSXML2.XMLHTTP")
                strUrl = "URL" & idno
    End If

    blnAsync = True

With objRequest
    .Open "GET", strUrl, blnAsync
    .setRequestHeader "Content-Type", "application/json"
    .send

                While objRequest.readyState <> 4
                    DoEvents
                Wend
                
    strResponse = .ResponseText
        
End With
Next

strResponse = ws.Cells(r, "A")

End Sub



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

然而,当我们谈论 contexts 时,值得以这种方式了解。

不过,您可以设计显式上下文类。在设计原型时,我更喜欢数据类,因为它很容易来回序列化它们。

干杯!