如何通过引用传递变量?

问题:如何通过引用传递变量?

Python文档似乎尚不清楚参数是通过引用还是通过值传递,并且以下代码产生不变的值“原始”

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

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

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

The Python documentation seems unclear about whether parameters are passed by reference or value, and the following code produces the unchanged value ‘Original’

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

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

Is there something I can do to pass the variable by actual reference?


回答 0

参数通过赋值传递。其背后的理由是双重的:

  1. 传入的参数实际上是对象的引用(但引用是按值传递的)
  2. 有些数据类型是可变的,但有些不是

所以:

  • 如果将可变对象传递给方法,则该方法将获得对该对象的引用,并且可以对其进行突变,但是如果您将该引用重新绑定到该方法中,则外部作用域对此一无所知完成后,外部参考仍将指向原始对象。

  • 如果将不可变对象传递给方法,则仍然无法重新绑定外部引用,甚至无法使对象发生突变。

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

列表-可变类型

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

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_list是副本outer_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_string是副本outer_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])

尽管这看起来有点麻烦。

Arguments are passed by assignment. The rationale behind this is twofold:

  1. the parameter passed in is actually a reference to an object (but the reference is passed by value)
  2. some data types are mutable, but others aren’t

So:

  • If you pass a mutable object into a method, the method gets a reference to that same object and you can mutate it to your heart’s delight, but if you rebind the reference in the method, the outer scope will know nothing about it, and after you’re done, the outer reference will still point at the original object.

  • If you pass an immutable object to a method, you still can’t rebind the outer reference, and you can’t even mutate the object.

To make it even more clear, let’s have some examples.

List – a mutable type

Let’s try to modify the list that was passed to a method:

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)

Output:

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

Since the parameter passed in is a reference to outer_list, not a copy of it, we can use the mutating list methods to change it and have the changes reflected in the outer scope.

Now let’s see what happens when we try to change the reference that was passed in as a parameter:

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)

Output:

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']

Since the the_list parameter was passed by value, assigning a new list to it had no effect that the code outside the method could see. The the_list was a copy of the outer_list reference, and we had the_list point to a new list, but there was no way to change where outer_list pointed.

String – an immutable type

It’s immutable, so there’s nothing we can do to change the contents of the string

Now, let’s try to change the reference

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)

Output:

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

Again, since the the_string parameter was passed by value, assigning a new string to it had no effect that the code outside the method could see. The the_string was a copy of the outer_string reference, and we had the_string point to a new string, but there was no way to change where outer_string pointed.

I hope this clears things up a little.

EDIT: It’s been noted that this doesn’t answer the question that @David originally asked, “Is there something I can do to pass the variable by actual reference?”. Let’s work on that.

How do we get around this?

As @Andrea’s answer shows, you could return the new value. This doesn’t change the way things are passed in, but does let you get the information you want back out:

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)

If you really wanted to avoid using a return value, you could create a class to hold your value and pass it into the function or use an existing class, like a list:

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])

Although this seems a little cumbersome.


回答 1

问题来自对Python中的变量有误解。如果您习惯了大多数传统语言,那么您将对以下顺序有一个心理模型:

a = 1
a = 2

您认为这a是存储值的存储位置1,然后将其更新以存储值2。这不是Python中的工作方式。相反,a首先将其作为对具有值的对象的引用1,然后将其重新分配为对具有value的对象的引用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'

The problem comes from a misunderstanding of what variables are in Python. If you’re used to most traditional languages, you have a mental model of what happens in the following sequence:

a = 1
a = 2

You believe that a is a memory location that stores the value 1, then is updated to store the value 2. That’s not how things work in Python. Rather, a starts as a reference to an object with the value 1, then gets reassigned as a reference to an object with the value 2. Those two objects may continue to coexist even though a doesn’t refer to the first one anymore; in fact they may be shared by any number of other references within the program.

When you call a function with a parameter, a new reference is created that refers to the object passed in. This is separate from the reference that was used in the function call, so there’s no way to update that reference and make it refer to a new object. In your example:

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

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

self.variable is a reference to the string object 'Original'. When you call Change you create a second reference var to the object. Inside the function you reassign the reference var to a different string object 'Changed', but the reference self.variable is separate and does not change.

The only way around this is to pass a mutable object. Because both references refer to the same object, any changes to the object are reflected in both places.

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

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

回答 2

我发现其他答案相当冗长和复杂,因此我创建了这个简单的图来解释Python处理变量和参数的方式。

I found the other answers rather long and complicated, so I created this simple diagram to explain the way Python treats variables and parameters.


回答 3

它既不是值传递也不是引用传递-它是对象调用。参见Fredrik Lundh的观点:

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

这是一个重要的报价:

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

在您的示例中,Change调用该方法时-为该方法创建了一个命名空间;并var在该命名空间中成为字符串object的名称'Original'。然后,该对象在两个命名空间中都有一个名称。接下来,var = 'Changed'绑定var到新的字符串对象,因此该方法的命名空间会忘记'Original'。最后,忘记了该命名空间,以及字符串'Changed'

It is neither pass-by-value or pass-by-reference – it is call-by-object. See this, by Fredrik Lundh:

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

Here is a significant quote:

“…variables [names] are not objects; they cannot be denoted by other variables or referred to by objects.”

In your example, when the Change method is called–a namespace is created for it; and var becomes a name, within that namespace, for the string object 'Original'. That object then has a name in two namespaces. Next, var = 'Changed' binds var to a new string object, and thus the method’s namespace forgets about 'Original'. Finally, that namespace is forgotten, and the string 'Changed' along with it.


回答 4

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

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

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

由于不可变类型无法修改,因此它们看起来像是通过值传递-将int传递给函数意味着将int分配给函数的参数。您只能重新分配它,但不会更改原始变量的值。

Think of stuff being passed by assignment instead of by reference/by value. That way, it is always clear, what is happening as long as you understand what happens during the normal assignment.

So, when passing a list to a function/method, the list is assigned to the parameter name. Appending to the list will result in the list being modified. Reassigning the list inside the function will not change the original list, since:

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

Since immutable types cannot be modified, they seem like being passed by value – passing an int into a function means assigning the int to the function’s parameter. You can only ever reassign that, but it won’t change the original variables value.


回答 5

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

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

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

  • 当您使用来更新“ x”时x = 2000,将创建一个新的整数对象,并且字典将更新为指向该新对象。旧的一千个对象保持不变(取决于其他是否引用了该对象,该对象是否可以存活)。

  • 当您进行诸如的新分配时y = x,将创建一个新的字典条目“ y”,该条目指向与“ x”条目相同的对象。

  • 诸如字符串和整数之类的对象是不可变的。这仅表示没有任何方法可以在创建对象后更改该对象。例如,一旦创建了整数对象1000,它就永远不会改变。数学是通过创建新的整数对象完成的。

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

希望能为您解决问题。

Effbot (aka Fredrik Lundh) has described Python’s variable passing style as call-by-object: http://effbot.org/zone/call-by-object.htm

Objects are allocated on the heap and pointers to them can be passed around anywhere.

  • When you make an assignment such as x = 1000, a dictionary entry is created that maps the string “x” in the current namespace to a pointer to the integer object containing one thousand.

  • When you update “x” with x = 2000, a new integer object is created and the dictionary is updated to point at the new object. The old one thousand object is unchanged (and may or may not be alive depending on whether anything else refers to the object).

  • When you do a new assignment such as y = x, a new dictionary entry “y” is created that points to the same object as the entry for “x”.

  • Objects like strings and integers are immutable. This simply means that there are no methods that can change the object after it has been created. For example, once the integer object one-thousand is created, it will never change. Math is done by creating new integer objects.

  • Objects like lists are mutable. This means that the contents of the object can be changed by anything pointing to the object. For example, x = []; y = x; x.append(10); print y will print [10]. The empty list was created. Both “x” and “y” point to the same list. The append method mutates (updates) the list object (like adding a record to a database) and the result is visible to both “x” and “y” (just as a database update would be visible to every connection to that database).

Hope that clarifies the issue for you.


回答 6

从技术上讲,Python始终使用按引用传递值。我将重复其他答案以支持我的发言。

Python始终使用按引用传递值。也不exceptions。任何变量分配都意味着复制参考值。没有exceptions。任何变量都是绑定到参考值的名称。总是。

您可以将参考值视为目标对象的地址。该地址在使用时会自动取消引用。这样,使用参考值,似乎您可以直接使用目标对象。但是,两者之间总是存在一个参考,距离目标还有一步之遥。

这是证明Python使用引用传递的示例:

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

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

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

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

Technically, Python always uses pass by reference values. I am going to repeat my other answer to support my statement.

Python always uses pass-by-reference values. There isn’t any exception. Any variable assignment means copying the reference value. No exception. Any variable is the name bound to the reference value. Always.

You can think about a reference value as the address of the target object. The address is automatically dereferenced when used. This way, working with the reference value, it seems you work directly with the target object. But there always is a reference in between, one step more to jump to the target.

Here is the example that proves that Python uses passing by reference:

If the argument was passed by value, the outer lst could not be modified. The green are the target objects (the black is the value stored inside, the red is the object type), the yellow is the memory with the reference value inside — drawn as the arrow. The blue solid arrow is the reference value that was passed to the function (via the dashed blue arrow path). The ugly dark yellow is the internal dictionary. (It actually could be drawn also as a green ellipse. The colour and the shape only says it is internal.)

You can use the id() built-in function to learn what the reference value is (that is, the address of the target object).

In compiled languages, a variable is a memory space that is able to capture the value of the type. In Python, a variable is a name (captured internally as a string) bound to the reference variable that holds the reference value to the target object. The name of the variable is the key in the internal dictionary, the value part of that dictionary item stores the reference value to the target.

Reference values are hidden in Python. There isn’t any explicit user type for storing the reference value. However, you can use a list element (or element in any other suitable container type) as the reference variable, because all containers do store the elements also as references to the target objects. In other words, elements are actually not contained inside the container — only the references to elements are.


回答 7

Python中没有变量

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

  1. Python具有名称和对象。
  2. 分配将名称绑定到对象。
  3. 将参数传递给函数还会将名称(函数的参数名称)绑定到对象。

这就是全部。可变性与这个问题无关。

例:

a = 1

这会将名称绑定a到具有值1的整数类型的对象。

b = x

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

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

如何阅读问题中的例子

在问题所示的代码中,该语句self.Change(self.variable)将名称var(在function的范围内Change)绑定到保存该值的对象,'Original'而赋值var = 'Changed'(在function的主体中Change)再次将同一个名称分配给其他对象(发生这种情况)也可以容纳一个字符串,但完全可以是其他东西)。

如何通过参考

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

如果它是一个不可变的对象(例如布尔,数字,字符串),则将其包装在一个可变的对象中。
对此的快速解决方案是一个元素列表(而不是self.variable,pass [self.variable]和函数Modify var[0])。
更加Python化的方法是引入一个琐碎的,一属性类。该函数接收该类的实例并操纵该属性。

There are no variables in Python

The key to understanding parameter passing is to stop thinking about “variables”. There are names and objects in Python and together they appear like variables, but it is useful to always distinguish the three.

  1. Python has names and objects.
  2. Assignment binds a name to an object.
  3. Passing an argument into a function also binds a name (the parameter name of the function) to an object.

That is all there is to it. Mutability is irrelevant to this question.

Example:

a = 1

This binds the name a to an object of type integer that holds the value 1.

b = x

This binds the name b to the same object that the name x is currently bound to. Afterward, the name b has nothing to do with the name x anymore.

See sections 3.1 and 4.2 in the Python 3 language reference.

How to read the example in the question

In the code shown in the question, the statement self.Change(self.variable) binds the name var (in the scope of function Change) to the object that holds the value 'Original' and the assignment var = 'Changed' (in the body of function Change) assigns that same name again: to some other object (that happens to hold a string as well but could have been something else entirely).

How to pass by reference

So if the thing you want to change is a mutable object, there is no problem, as everything is effectively passed by reference.

If it is an immutable object (e.g. a bool, number, string), the way to go is to wrap it in a mutable object.
The quick-and-dirty solution for this is a one-element list (instead of self.variable, pass [self.variable] and in the function modify var[0]).
The more pythonic approach would be to introduce a trivial, one-attribute class. The function receives an instance of the class and manipulates the attribute.


回答 8

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

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

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

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

A simple trick I normally use is to just wrap it in a list:

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

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

(Yeah I know this can be inconvenient, but sometimes it is simple enough to do this.)


回答 9

(编辑-布莱尔已更新了他的非常受欢迎的答案,因此现在是准确的)

我认为重要的是要注意,当前职位(布莱尔·康拉德(Blair Conrad))的票数最高,尽管其结果正确无误,但根据其定义,这是误导性的,也是不正确的。尽管有许多语言(例如C)允许用户通过引用传递或通过值传递,但Python并不是其中一种。

大卫·库纳波(David Cournapeau)的答案指出了真正的答案,并解释了为什么布莱尔·康拉德(Blair Conrad)的帖子中的行为似乎是正确的,而定义却不正确。

就Python按值传递的程度而言,所有语言都是按值传递的,因为必须发送一些数据(“值”或“引用”)。但是,这并不意味着Python就象C程序员会想到的那样按值传递。

如果您想要该行为,Blair Conrad的答案很好。但是,如果您想了解为什么Python既不按值传递也不按引用传递的细节,请阅读David Cournapeau的答案。

(edit – Blair has updated his enormously popular answer so that it is now accurate)

I think it is important to note that the current post with the most votes (by Blair Conrad), while being correct with respect to its result, is misleading and is borderline incorrect based on its definitions. While there are many languages (like C) that allow the user to either pass by reference or pass by value, Python is not one of them.

David Cournapeau’s answer points to the real answer and explains why the behavior in Blair Conrad’s post seems to be correct while the definitions are not.

To the extent that Python is pass by value, all languages are pass by value since some piece of data (be it a “value” or a “reference”) must be sent. However, that does not mean that Python is pass by value in the sense that a C programmer would think of it.

If you want the behavior, Blair Conrad’s answer is fine. But if you want to know the nuts and bolts of why Python is neither pass by value or pass by reference, read David Cournapeau’s answer.


回答 10

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

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' )

You got some really good answers here.

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

在这种情况下,var方法中标题为该变量Change的引用将分配给self.variable,然后您立即将一个字符串分配给var。它不再指向self.variable。以下代码段显示了如果修改由var和指向的数据结构self.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']
>>> 

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

In this case the variable titled var in the method Change is assigned a reference to self.variable, and you immediately assign a string to var. It’s no longer pointing to self.variable. The following code snippet shows what would happen if you modify the data structure pointed to by var and self.variable, in this case a list:

>>> 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']
>>> 

I’m sure someone else could clarify this further.


回答 12

Python的传递分配方案与C ++的引用参数选项不太相同,但实际上与C语言(和其他语言)的参数传递模型非常相似:

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

Python’s pass-by-assignment scheme isn’t quite the same as C++’s reference parameters option, but it turns out to be very similar to the argument-passing model of the C language (and others) in practice:

  • Immutable arguments are effectively passed “by value.” Objects such as integers and strings are passed by object reference instead of by copying, but because you can’t change immutable objects in place anyhow, the effect is much like making a copy.
  • Mutable arguments are effectively passed “by pointer.” Objects such as lists and dictionaries are also passed by object reference, which is similar to the way C passes arrays as pointers—mutable objects can be changed in place in the function, much like C arrays.

回答 13

如您所言,您需要有一个可变对象,但让我建议您检查一下全局变量,因为它们可以帮助您甚至解决此类问题!

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

As you can state you need to have a mutable object, but let me suggest you to check over the global variables as they can help you or even solve this kind of issue!

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

example:

>>> 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

答案中有很多见解,但我认为此处没有明确提及其他要点。引用python文档https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

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

即使将可变对象传递给函数,这仍然适用。并且向我清楚地解释了在函数中分配给对象和对对象进行操作之间行为不同的原因。

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

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

A lot of insights in answers here, but i think an additional point is not clearly mentioned here explicitly. Quoting from python documentation https://docs.python.org/2/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python

“In Python, variables that are only referenced inside a function are implicitly global. If a variable is assigned a new value anywhere within the function’s body, it’s assumed to be a local. If a variable is ever assigned a new value inside the function, the variable is implicitly local, and you need to explicitly declare it as ‘global’. Though a bit surprising at first, a moment’s consideration explains this. On one hand, requiring global for assigned variables provides a bar against unintended side-effects. On the other hand, if global was required for all global references, you’d be using global all the time. You’d have to declare as global every reference to a built-in function or to a component of an imported module. This clutter would defeat the usefulness of the global declaration for identifying side-effects.”

Even when passing a mutable object to a function this still applies. And to me clearly explains the reason for the difference in behavior between assigning to the object and operating on the object in the function.

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)

gives:

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

The assignment to an global variable that is not declared global therefore creates a new local object and breaks the link to the original object.


回答 15

这是pass by objectPython中使用的概念的简单解释(我希望如此)。
每当将对象传递给函数时,都会传递对象本身(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)

这显然会改变对象。这个答案很好地解释了。

Here is the simple (I hope) explanation of the concept pass by object used in Python.
Whenever you pass an object to the function, the object itself is passed (object in Python is actually what you’d call a value in other programming languages) not the reference to this object. In other words, when you call:

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

my_list = [0, 1]
change_me(my_list)

The actual object – [0, 1] (which would be called a value in other programming languages) is being passed. So in fact the function change_me will try to do something like:

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

which obviously will not change the object passed to the function. If the function looked like this:

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

Then the call would result in:

[0, 1].append(2)

which obviously will change the object. This answer explains it well.


回答 16

除了对这些东西如何在Python中工作的所有出色解释之外,我没有看到关于该问题的简单建议。正如您似乎确实要创建对象和实例一样,处理实例变量并更改它们的pythonic方法如下:

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

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

在实例方法中,通常引用self访问实例属性。通常__init__在实例方法中设置实例属性并对其进行读取或更改。这也是为什么您将selfals的第一个参数传递给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

Aside from all the great explanations on how this stuff works in Python, I don’t see a simple suggestion for the problem. As you seem to do create objects and instances, the pythonic way of handling instance variables and changing them is the following:

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

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

In instance methods, you normally refer to self to access instance attributes. It is normal to set instance attributes in __init__ and read or change them in instance methods. That is also why you pass self als the first argument to def Change.

Another solution would be to create a static method like this:

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

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

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

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

There is a little trick to pass an object by reference, even though the language doesn’t make it possible. It works in Java too, it’s the list with one item. ;-)

class PassByReference:
    def __init__(self, name):
        self.name = name

def changeRef(ref):
    ref[0] = PassByReference('Michael')

obj = PassByReference('Peter')
print obj.name

p = [obj] # A pointer to obj! ;-)
changeRef(p)

print p[0].name # p->name

It’s an ugly hack, but it works. ;-P


回答 18

我使用以下方法快速将几个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

I used the following method to quickly convert a couple of Fortran codes to Python. True, it’s not pass by reference as the original question was posed, but is a simple work around in some cases.

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

虽然按引用传递并不是最适合python的东西,并且不应该使用,但是有些变通办法实际上可以起作用,以获取当前分配给局部变量的对象,或者甚至从调用函数内部重新分配局部变量。

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

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

def change(wrapper):
    wrapper(7)

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

相同的想法适用于读取和del设置变量。

对于正读,甚至有一种更短的使用方法,lambda: x该方法返回可调用对象,而被调用时将返回x的当前值。这有点像遥远的过去在语言中使用的“按名称呼叫”。

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

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

借助Python的“反射”支持,可以获得一个对象,该对象能够在给定范围内重新分配名称/变量,而无需在该范围内显式定义函数:

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文档建议更改字典可能不起作用,但似乎对我有用。

While pass by reference is nothing that fits well into python and should be rarely used there are some workarounds that actually can work to get the object currently assigned to a local variable or even reassign a local variable from inside of a called function.

The basic idea is to have a function that can do that access and can be passed as object into other functions or stored in a class.

One way is to use global (for global variables) or nonlocal (for local variables in a function) in a wrapper function.

def change(wrapper):
    wrapper(7)

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

The same idea works for reading and deleting a variable.

For just reading there is even a shorter way of just using lambda: x which returns a callable that when called returns the current value of x. This is somewhat like “call by name” used in languages in the distant past.

Passing 3 wrappers to access a variable is a bit unwieldy so those can be wrapped into a class that has a proxy attribute:

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

Pythons “reflection” support makes it possible to get a object that is capable of reassigning a name/variable in a given scope without defining functions explicitly in that scope:

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)

Here the ByRef class wraps a dictionary access. So attribute access to wrapped is translated to a item access in the passed dictionary. By passing the result of the builtin locals and the name of a local variable this ends up accessing a local variable. The python documentation as of 3.5 advises that changing the dictionary might not work but it seems to work for me.


回答 20

给定python处理值和对其的引用的方式,您可以引用任意实例属性的唯一方法是按名称进行:

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

    def change(self, var):
        self.__dict__[var] = 'Changed'

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

given the way python handles values and references to them, the only way you can reference an arbitrary instance attribute is by name:

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

    def change(self, var):
        self.__dict__[var] = 'Changed'

in real code you would, of course, add error checking on the dict lookup.


回答 21

由于字典是通过引用传递的,因此可以使用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

Since dictionaries are passed by reference, you can use a dict variable to store any referenced values inside it.

# 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

回答 22

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

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

Pass-By-Reference in Python is quite different from the concept of pass by reference in C++/Java.

  • Java&C#: primitive types(include string)pass by value(copy), Reference type is passed by reference(address copy) so all changes made in the parameter in the called function are visible to the caller.
  • C++: Both pass-by-reference or pass-by-value are allowed. If a parameter is passed by reference, you can either modify it or not depending upon whether the parameter was passed as const or not. However, const or not, the parameter maintains the reference to the object and reference cannot be assigned to point to a different object within the called function.
  • Python: Python is “pass-by-object-reference”, of which it is often said: “Object references are passed by value.”[Read here]1. Both the caller and the function refer to the same object but the parameter in the function is a new variable which is just holding a copy of the object in the caller. Like C++, a parameter can be either modified or not in function – This depends upon the type of object passed. eg; An immutable object type cannot be modified in the called function whereas a mutable object can be either updated or re-initialized. A crucial difference between updating or re-assigning/re-initializing the mutable variable is that updated value gets reflected back in the called function whereas the reinitialized value does not. Scope of any assignment of new object to a mutable variable is local to the function in the python. Examples provided by @blair-conrad are great to understand this.

回答 23

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

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'

Since your example happens to be object-oriented, you could make the following change to achieve a similar result:

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'

回答 24

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

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)

You can merely use an empty class as an instance to store reference objects because internally object attributes are stored in an instance dictionary. See the example.

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)

回答 25

由于似乎没有地方提到,例如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

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

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

Since it seems to be nowhere mentioned an approach to simulate references as known from e.g. C++ is to use an “update” function and pass that instead of the actual variable (or rather, “name”):

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

This is mostly useful for “out-only references” or in a situation with multiple threads / processes (by making the update function thread / multiprocessing safe).

Obviously the above does not allow reading the value, only updating it.