如何通过引用传递变量?

时间:2022-04-07 23:18:24

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

Python文档似乎不清楚参数是通过引用还是值传递的,下面的代码生成了未更改的值“原始”

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?

我能做些什么来通过实际的引用来传递这个变量吗?

22 个解决方案

#1


2125  

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. 传入的参数实际上是对对象的引用(但引用是按值传递的)
  3. some data types are mutable, but others aren't
  4. 有些数据类型是可变的,但其他的不是。

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.

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

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.

由于the_list参数是按值传递的,因此给它分配一个新列表,它没有影响到方法之外的代码可以看到的效果。the_list是outer_list引用的一个副本,我们将_list指向一个新列表,但是没有办法改变outer_list指向的位置。

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.

同样,由于the_string参数是按值传递的,所以为它分配一个新字符串不会影响方法之外的代码可以看到的效果。the_string是一个outer_string引用的副本,我们将_string指向一个新字符串,但是没有办法改变outer_string指向的位置。

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.

编辑:有人注意到,这并没有回答@David最初提出的问题:“我是否可以通过实际的引用来传递这个变量?”让我们一起。

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:

正如@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)

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.

尽管这看起来有点麻烦。

#2


497  

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:

这个问题来自于对Python中哪些变量的误解。如果你习惯了大多数的传统语言,你就会有一种思维模式:

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.

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

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.

自我。变量是对字符串对象“原始”的引用。当您调用Change时,您将为对象创建第二个reference var。在函数内部,您将引用var重新分配到一个不同的字符串对象“Changed”,但是引用self。变量是独立的,不会改变。

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'

#3


222  

I found the other answers rather long and complicated, so I created this simple diagram to explain the way Python treats variables and parameters. 如何通过引用传递变量?

我发现其他的答案相当长且复杂,所以我创建了这个简单的图来解释Python对待变量和参数的方式。

#4


211  

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

它既不是传递值,也不是传递引用——它是一个被调用的对象。看看这个,Fredrik Lundh:

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

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.

在您的示例中,当调用Change方法时——为它创建一个名称空间;在这个名称空间中,var成为字符串对象的“原始”名称。该对象在两个名称空间中有一个名称。接下来,var = 'Changed'将var绑定到一个新的字符串对象,因此该方法的命名空间会忘记'Original'。最后,这个名称空间被遗忘了,字符串也随之改变。

#5


134  

Think of stuff being passed by assignment instead of by reference/by value. That way, it is allways clear, what is happening as long as you understand what happens during 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 functions parameter. You can only ever reassign that, but it won't change the originial variables value.

由于不可变类型不能被修改,所以它们似乎是通过值传递的——将int传递给函数意味着将int赋值给函数参数。你只能重新分配它,但它不会改变原始变量的值。

#6


50  

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

Effbot(又名Fredrik Lundh)将Python的变量传递样式描述为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.

    当您执行诸如x = 1000这样的赋值时,将创建一个字典条目,该条目将当前名称空间中的字符串“x”映射到包含1000的整数对象的指针。

  • 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).

    当您使用x = 2000更新“x”时,会创建一个新的整数对象,并更新字典以指向新对象。旧的1000个对象没有变化(根据是否有其他的东西指代对象,它可能是活的,也可能是不存在的)。

  • 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".

    当您执行一个新的赋值,例如y = x时,将创建一个新的字典条目“y”,指向与“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.

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

  • 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).

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

Hope that clarifies the issue for you.

希望能澄清这个问题。

#7


50  

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

从技术上讲,Python总是使用引用值的传递。我将重复我的另一个答案来支持我的陈述。

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.

Python总是使用传递引用值。没有任何例外。任何变量赋值都意味着复制引用值。不例外。任何变量都是与引用值绑定的名称。总是这样。

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:

这里有一个例子,证明Python使用通过引用传递:

如何通过引用传递变量?

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

如果参数通过值传递,则不能修改外部lst。绿色是目标对象(黑色是存储在里面的值,红色是对象类型),黄色是内存中的引用值——绘制为箭头。蓝色实线箭头是传递给函数的引用值(通过虚线的蓝色箭头路径)。丑陋的暗黄色是内部字典。(它实际上也可以画成一个绿色椭圆。颜色和形状只说明它是内部的。

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

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

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.

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

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.

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

#8


34  

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


32  

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

我认为重要的是要注意到,目前以多数票(由布莱尔·康拉德(Blair Conrad)担任)的职位,虽然在其结果上是正确的,但却具有误导性,而且根据其定义是不正确的。虽然有许多语言(比如C)允许用户通过引用或通过值传递,但是Python不是其中之一。

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.

David Cournapeau的回答指向了真实的答案,并解释了为什么在布莱尔·康拉德的文章中,这种行为似乎是正确的,而定义却并非如此。

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.

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

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.

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

#10


22  

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.

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

  1. Python has names and objects.
  2. Python有名称和对象。
  3. Assignment binds a name to an object.
  4. 赋值将名称绑定到对象。
  5. Passing an argument into a function also binds a name (the parameter name of the function) to an object.
  6. 将参数传递给函数还将名称(函数的参数名称)绑定到对象。

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

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

Example:

例子:

a = 1

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

这将名称a绑定到持有值1的integer类型对象。

b = x

This binds the name b to the same object that the name x is currently bound to. Afterwards, the name b has nothing to do with the name x any more.

这将名称b绑定到名称x当前绑定到的同一对象上。之后,名称b与x的名称没有任何关系。

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

参见Python 3语言引用中的3.1和4.2节。


So 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).

中所示的代码问题,声明self.Change(self.variable)结合var的名称(在函数变化的范围)的对象持有“原始”和价值分配var =“改变”(在函数变化)的身体再次分配相同的名字:其他对象(这发生在举行一个字符串,但可能是完全不同的东西)。

#11


19  

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

#12


14  

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:

在本例中,方法更改中名为var的变量被分配给self。变量,然后你立即将一个字符串赋给var,它不再指向self。variable。下面的代码片段显示了如果修改由var和self指向的数据结构会发生什么。变量,在这里是一个列表:

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

我相信其他人可以进一步阐明这一点。

#13


13  

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:

Python的传递分配方案与c++的参考参数选项不太一样,但实际上它与C语言(和其他)在实践中使用的参数传递模型非常相似:

  • 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.
  • 可变参数通过指针有效传递。诸如列表和字典之类的对象也通过对象引用传递,这与C传递数组的方式类似,因为pointers -可变对象可以在函数中进行更改,就像C数组一样。

#14


12  

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

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

#15


10  

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

这里有很多关于答案的见解,但我认为这里没有明确提到另外一点。引用python文档https://docs.python.org/2/faq/programming.html#什么是本地和全局变量的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."

在Python中,仅在函数内引用的变量是隐式全局变量。如果在函数的主体内的任何地方都分配了一个新值,则假定该变量是本地的。如果一个变量在函数内被赋值一个新值,那么这个变量就是本地的,你需要显式地将它声明为“全局”。虽然一开始有点让人吃惊,但考虑到这一点,我们还是要考虑一下。一方面,要求全局变量为指定变量提供了一个防止意外副作用的障碍。另一方面,如果所有全局引用都需要全局变量,那么您将一直使用全局变量。您必须声明为全局的每个引用到一个内置函数或导入模块的一个组件。这一混乱将会挫败全球识别副作用宣言的有效性。

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.

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

#16


6  

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:

这里有一个简单的(我希望)解释Python中使用的对象的概念。当您将对象传递给函数时,对象本身就会被传递(Python中的对象实际上就是您所调用的其他编程语言中的值),而不是该对象的引用。换句话说,当你打电话:

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](在其他编程语言中称为值)正在被传递。实际上,change_me会尝试做如下的事情:

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

这显然会改变物体。这个答案解释得很好。

#17


4  

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. ;-)

有一个小技巧可以通过引用传递一个对象,尽管该语言没有使它成为可能。它也在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

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

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

#18


4  

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:

除了所有关于这个东西如何在Python中工作的伟大解释之外,我没有看到一个简单的关于这个问题的建议。您似乎确实创建了对象和实例,处理实例变量和更改实例变量的python方法如下:

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.

在实例方法中,您通常引用self来访问实例属性。在__init__中设置实例属性并在实例方法中读取或更改它们是正常的。这也是为什么你要传递self的第一个参数到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

#19


3  

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.

我使用下面的方法快速地将一些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

#20


2  

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.

虽然pass by引用并不是很适合于python,但应该很少使用,但实际上有一些变通方法可以将当前分配给局部变量的对象,甚至从调用函数的内部重新分配一个局部变量。

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.

对于仅仅阅读,甚至还有一种更短的方法来使用lambda: x,它返回一个callable,当调用返回当前x的值时,这有点像在遥远的过去的语言中使用的“名称调用”。

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

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

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:

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)

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.

这里ByRef类包装了一个字典访问。因此,对包装的属性访问被转换为通过的字典中的项访问。通过传递builtin本地变量的结果和本地变量的名称,最终会访问一个局部变量。3.5的python文档建议,修改字典可能行不通,但它似乎对我有用。

#21


2  

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

给定python处理值和引用的方式,您可以引用任意实例属性的惟一方法是:

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.

当然,在实际代码中,您可能会在字典查找中添加错误检查。

#22


1  

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'

#1


2125  

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. 传入的参数实际上是对对象的引用(但引用是按值传递的)
  3. some data types are mutable, but others aren't
  4. 有些数据类型是可变的,但其他的不是。

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.

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

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.

由于the_list参数是按值传递的,因此给它分配一个新列表,它没有影响到方法之外的代码可以看到的效果。the_list是outer_list引用的一个副本,我们将_list指向一个新列表,但是没有办法改变outer_list指向的位置。

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.

同样,由于the_string参数是按值传递的,所以为它分配一个新字符串不会影响方法之外的代码可以看到的效果。the_string是一个outer_string引用的副本,我们将_string指向一个新字符串,但是没有办法改变outer_string指向的位置。

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.

编辑:有人注意到,这并没有回答@David最初提出的问题:“我是否可以通过实际的引用来传递这个变量?”让我们一起。

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:

正如@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)

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.

尽管这看起来有点麻烦。

#2


497  

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:

这个问题来自于对Python中哪些变量的误解。如果你习惯了大多数的传统语言,你就会有一种思维模式:

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.

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

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.

自我。变量是对字符串对象“原始”的引用。当您调用Change时,您将为对象创建第二个reference var。在函数内部,您将引用var重新分配到一个不同的字符串对象“Changed”,但是引用self。变量是独立的,不会改变。

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'

#3


222  

I found the other answers rather long and complicated, so I created this simple diagram to explain the way Python treats variables and parameters. 如何通过引用传递变量?

我发现其他的答案相当长且复杂,所以我创建了这个简单的图来解释Python对待变量和参数的方式。

#4


211  

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

它既不是传递值,也不是传递引用——它是一个被调用的对象。看看这个,Fredrik Lundh:

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

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.

在您的示例中,当调用Change方法时——为它创建一个名称空间;在这个名称空间中,var成为字符串对象的“原始”名称。该对象在两个名称空间中有一个名称。接下来,var = 'Changed'将var绑定到一个新的字符串对象,因此该方法的命名空间会忘记'Original'。最后,这个名称空间被遗忘了,字符串也随之改变。

#5


134  

Think of stuff being passed by assignment instead of by reference/by value. That way, it is allways clear, what is happening as long as you understand what happens during 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 functions parameter. You can only ever reassign that, but it won't change the originial variables value.

由于不可变类型不能被修改,所以它们似乎是通过值传递的——将int传递给函数意味着将int赋值给函数参数。你只能重新分配它,但它不会改变原始变量的值。

#6


50  

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

Effbot(又名Fredrik Lundh)将Python的变量传递样式描述为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.

    当您执行诸如x = 1000这样的赋值时,将创建一个字典条目,该条目将当前名称空间中的字符串“x”映射到包含1000的整数对象的指针。

  • 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).

    当您使用x = 2000更新“x”时,会创建一个新的整数对象,并更新字典以指向新对象。旧的1000个对象没有变化(根据是否有其他的东西指代对象,它可能是活的,也可能是不存在的)。

  • 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".

    当您执行一个新的赋值,例如y = x时,将创建一个新的字典条目“y”,指向与“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.

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

  • 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).

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

Hope that clarifies the issue for you.

希望能澄清这个问题。

#7


50  

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

从技术上讲,Python总是使用引用值的传递。我将重复我的另一个答案来支持我的陈述。

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.

Python总是使用传递引用值。没有任何例外。任何变量赋值都意味着复制引用值。不例外。任何变量都是与引用值绑定的名称。总是这样。

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:

这里有一个例子,证明Python使用通过引用传递:

如何通过引用传递变量?

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

如果参数通过值传递,则不能修改外部lst。绿色是目标对象(黑色是存储在里面的值,红色是对象类型),黄色是内存中的引用值——绘制为箭头。蓝色实线箭头是传递给函数的引用值(通过虚线的蓝色箭头路径)。丑陋的暗黄色是内部字典。(它实际上也可以画成一个绿色椭圆。颜色和形状只说明它是内部的。

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

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

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.

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

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.

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

#8


34  

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


32  

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

我认为重要的是要注意到,目前以多数票(由布莱尔·康拉德(Blair Conrad)担任)的职位,虽然在其结果上是正确的,但却具有误导性,而且根据其定义是不正确的。虽然有许多语言(比如C)允许用户通过引用或通过值传递,但是Python不是其中之一。

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.

David Cournapeau的回答指向了真实的答案,并解释了为什么在布莱尔·康拉德的文章中,这种行为似乎是正确的,而定义却并非如此。

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.

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

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.

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

#10


22  

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.

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

  1. Python has names and objects.
  2. Python有名称和对象。
  3. Assignment binds a name to an object.
  4. 赋值将名称绑定到对象。
  5. Passing an argument into a function also binds a name (the parameter name of the function) to an object.
  6. 将参数传递给函数还将名称(函数的参数名称)绑定到对象。

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

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

Example:

例子:

a = 1

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

这将名称a绑定到持有值1的integer类型对象。

b = x

This binds the name b to the same object that the name x is currently bound to. Afterwards, the name b has nothing to do with the name x any more.

这将名称b绑定到名称x当前绑定到的同一对象上。之后,名称b与x的名称没有任何关系。

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

参见Python 3语言引用中的3.1和4.2节。


So 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).

中所示的代码问题,声明self.Change(self.variable)结合var的名称(在函数变化的范围)的对象持有“原始”和价值分配var =“改变”(在函数变化)的身体再次分配相同的名字:其他对象(这发生在举行一个字符串,但可能是完全不同的东西)。

#11


19  

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

#12


14  

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:

在本例中,方法更改中名为var的变量被分配给self。变量,然后你立即将一个字符串赋给var,它不再指向self。variable。下面的代码片段显示了如果修改由var和self指向的数据结构会发生什么。变量,在这里是一个列表:

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

我相信其他人可以进一步阐明这一点。

#13


13  

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:

Python的传递分配方案与c++的参考参数选项不太一样,但实际上它与C语言(和其他)在实践中使用的参数传递模型非常相似:

  • 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.
  • 可变参数通过指针有效传递。诸如列表和字典之类的对象也通过对象引用传递,这与C传递数组的方式类似,因为pointers -可变对象可以在函数中进行更改,就像C数组一样。

#14


12  

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

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

#15


10  

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

这里有很多关于答案的见解,但我认为这里没有明确提到另外一点。引用python文档https://docs.python.org/2/faq/programming.html#什么是本地和全局变量的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."

在Python中,仅在函数内引用的变量是隐式全局变量。如果在函数的主体内的任何地方都分配了一个新值,则假定该变量是本地的。如果一个变量在函数内被赋值一个新值,那么这个变量就是本地的,你需要显式地将它声明为“全局”。虽然一开始有点让人吃惊,但考虑到这一点,我们还是要考虑一下。一方面,要求全局变量为指定变量提供了一个防止意外副作用的障碍。另一方面,如果所有全局引用都需要全局变量,那么您将一直使用全局变量。您必须声明为全局的每个引用到一个内置函数或导入模块的一个组件。这一混乱将会挫败全球识别副作用宣言的有效性。

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.

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

#16


6  

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:

这里有一个简单的(我希望)解释Python中使用的对象的概念。当您将对象传递给函数时,对象本身就会被传递(Python中的对象实际上就是您所调用的其他编程语言中的值),而不是该对象的引用。换句话说,当你打电话:

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](在其他编程语言中称为值)正在被传递。实际上,change_me会尝试做如下的事情:

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

这显然会改变物体。这个答案解释得很好。

#17


4  

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. ;-)

有一个小技巧可以通过引用传递一个对象,尽管该语言没有使它成为可能。它也在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

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

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

#18


4  

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:

除了所有关于这个东西如何在Python中工作的伟大解释之外,我没有看到一个简单的关于这个问题的建议。您似乎确实创建了对象和实例,处理实例变量和更改实例变量的python方法如下:

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.

在实例方法中,您通常引用self来访问实例属性。在__init__中设置实例属性并在实例方法中读取或更改它们是正常的。这也是为什么你要传递self的第一个参数到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

#19


3  

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.

我使用下面的方法快速地将一些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

#20


2  

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.

虽然pass by引用并不是很适合于python,但应该很少使用,但实际上有一些变通方法可以将当前分配给局部变量的对象,甚至从调用函数的内部重新分配一个局部变量。

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.

对于仅仅阅读,甚至还有一种更短的方法来使用lambda: x,它返回一个callable,当调用返回当前x的值时,这有点像在遥远的过去的语言中使用的“名称调用”。

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

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

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:

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)

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.

这里ByRef类包装了一个字典访问。因此,对包装的属性访问被转换为通过的字典中的项访问。通过传递builtin本地变量的结果和本地变量的名称,最终会访问一个局部变量。3.5的python文档建议,修改字典可能行不通,但它似乎对我有用。

#21


2  

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

给定python处理值和引用的方式,您可以引用任意实例属性的惟一方法是:

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.

当然,在实际代码中,您可能会在字典查找中添加错误检查。

#22


1  

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'