[英]How do I pass a variable by reference?
Are parameters passed by reference or by value?参数是按引用传递还是按值传递? How do I pass by reference so that the code below outputs
'Changed'
instead of 'Original'
?我如何通过引用传递以便下面的代码输出
'Changed'
而不是'Original'
?
class PassByReference:
def __init__(self):
self.variable = 'Original'
self.change(self.variable)
print(self.variable)
def change(self, var):
var = 'Changed'
Arguments are passed by assignment .参数通过 assignment 传递。 The rationale behind this is twofold:
这背后的理由是双重的:
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.为了更清楚,让我们举一些例子。
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
的引用,而不是它的副本,我们可以使用 mutating 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_list
参数是按值传递的,因此为它分配一个新列表对方法外部的代码无法看到的影响。 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
是outer_list
引用的副本,我们让the_list
指向一个新列表,但无法更改outer_list
指向的位置。
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_string
参数是按值传递的,因此为它分配一个新字符串对方法外部的代码没有任何影响。 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
是outer_string
引用的副本,我们让the_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?".编辑:有人指出,这并没有回答@David 最初提出的问题,“我可以做些什么来通过实际引用传递变量吗?”。 Let's work on that.
让我们继续努力。
As @Andrea's answer shows, you could return the new value.正如@Andrea 的回答所示,您可以返回新值。 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.虽然这看起来有点麻烦。
The problem comes from a misunderstanding of what variables are in Python.问题来自对 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
.您认为
a
是存储值1
的内存位置,然后更新为存储值2
。 That's not how things work in Python.这不是 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
.相反,
a
开始是对值为1
的对象的引用,然后被重新分配为对值为2
的对象的引用。 Those two objects may continue to coexist even though a
doesn't refer to the first one anymore;即使
a
不再引用第一个对象,这两个对象也可能继续共存; 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'
. self.variable
是对字符串对象'Original'
的引用。 When you call Change
you create a second reference var
to the object.当您调用
Change
时,您会为该对象创建第二个引用var
。 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.在函数内部,您将引用
var
重新分配给不同的字符串对象'Changed'
,但引用self.variable
是独立的并且不会更改。
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'
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;在您的示例中,当调用
Change
方法时,会为其创建一个命名空间; and var
becomes a name, within that namespace, for the string object 'Original'
.并且
var
成为该名称空间内字符串对象'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'
.接下来,
var = 'Changed'
将var
绑定到一个新的字符串对象,因此方法的命名空间忘记了'Original'
。 Finally, that namespace is forgotten, and the string 'Changed'
along with it.最后,这个命名空间被遗忘了,字符串
'Changed'
也被遗忘了。
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.由于无法修改不可变类型,它们似乎是按值传递的——将 int 传递给函数意味着将 int 分配给函数的参数。 You can only ever reassign that, but it won't change the original variables value.
您只能重新分配它,但它不会更改原始变量值。
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 中有名称和对象,它们一起看起来像变量,但始终区分这三者是有用的。
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.这将名称
a
绑定到具有值 1 的整数类型的对象。
b = x
This binds the name b
to the same object that the name x
is currently bound to.这将名称
b
绑定到名称x
当前绑定到的同一对象。 Afterward, the name b
has nothing to do with the name x
anymore.之后,名称
b
就与名称x
无关了。
See sections 3.1 and 4.2 in the Python 3 language reference.请参阅 Python 3 语言参考中的第3.1和4.2节。
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
(在函数Change
的范围内)绑定到保存值'Original'
和赋值var = 'Changed'
对象(在函数体Change
)再次将相同的名称分配给其他对象(它恰好也包含一个字符串,但可能完全是其他东西)。
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 (eg 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]
).快速而简单的解决方案是一个单元素列表(而不是
self.variable
,传递[self.variable]
并在函数中修改var[0]
)。
The more pythonic approach would be to introduce a trivial, one-attribute class.更Pythonic的方法是引入一个简单的、单一属性的类。 The function receives an instance of the class and manipulates the attribute.
该函数接收该类的一个实例并操作该属性。
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 的变量传递风格描述为按对象调用: 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”映射到指向包含一千的整数对象的指针。
When you update "x" with x = 2000
, a new integer object is created and the dictionary is updated to point at the new object.当您使用
x = 2000
更新 "x" 时,会创建一个新的整数对象,并且字典会更新为指向新对象。 The old one thousand object is unchanged (and may or may not be alive depending on whether anything else refers to the object).旧的 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.
数学是通过创建新的整数对象来完成的。
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
例如,
x = []; y = x; x.append(10); print y
x = []; y = x; x.append(10); print y
x = []; y = x; x.append(10); print y
will print [10]
. x = []; y = x; x.append(10); print y
将打印[10]
。 The empty list was created.空列表已创建。 Both "x" and "y" point to the same list.
“x”和“y”都指向同一个列表。 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).
append方法改变(更新)列表对象(如向数据库中添加记录),结果对“x”和“y”都可见(就像数据库更新对该数据库的每个连接都是可见的)。
Hope that clarifies the issue for you.希望能为您澄清问题。
Technically, Python always uses pass by reference values .从技术上讲, Python 总是使用通过引用值传递。 I am going to repeat my other answer to support my statement.
我将重复我的其他答案以支持我的陈述。
Python always uses pass-by-reference values. Python 总是使用传递引用值。 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:这是证明 Python 使用按引用传递的示例:
If the argument was passed by value, the outer lst
could not be modified.如果参数是按值传递的,则无法修改外部
lst
。 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).您可以使用
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.
在 Python 中,变量是绑定到引用变量的名称(内部捕获为字符串),该变量保存目标对象的引用值。 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.参考值隐藏在 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.
换句话说,元素实际上并不包含在容器中——只有对元素的引用才是。
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.) (是的,我知道这可能很不方便,但有时这样做很简单。)
(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.
虽然有许多语言(如 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 的回答指向了真正的答案,并解释了为什么 Blair Conrad 的帖子中的行为似乎是正确的,而定义却不是。
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.在 Python 是按值传递的范围内,所有语言都是按值传递的,因为必须发送一些数据(无论是“值”还是“引用”)。 However, that does not mean that Python is pass by value in the sense that a C programmer would think of it.
但是,这并不意味着 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 的回答。
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' )
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 语言(和其他语言)的参数传递模型非常相似:
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
.在这种情况下,方法
Change
中名为var
的变量被分配了对self.variable
的引用,并且您立即将字符串分配给var
。 It's no longer pointing to self.variable
.它不再指向
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.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']
>>>
I'm sure someone else could clarify this further.我相信其他人可以进一步澄清这一点。
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
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#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." “在 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.因此,对未声明为 global 的全局变量的赋值会创建一个新的局部对象并断开与原始对象的链接。
Here is the simple (I hope) explanation of the concept pass by object
used in Python.这是 Python 中使用
pass by object
概念的简单(我希望)解释。
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.每当您将对象传递给函数时,传递的是对象本身(Python 中的对象实际上是您在其他编程语言中所称的值)而不是对该对象的引用。 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.实际对象 - [0, 1](在其他编程语言中称为值)正在传递。 So in fact the function
change_me
will try to do something like:所以实际上函数
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.
这个答案很好地解释了它。
Aside from all the great explanations on how this stuff works in Python, I don't see a simple suggestion for the problem.除了关于这些东西如何在 Python 中工作的所有很好的解释之外,我没有看到针对该问题的简单建议。 As you seem to do create objects and instances, the pythonic way of handling instance variables and changing them is the following:
正如您似乎确实创建了对象和实例,处理实例变量和更改它们的pythonic方法如下:
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.在实例方法中,您通常引用
self
来访问实例属性。 It is normal to set instance attributes in __init__
and read or change them in instance methods.在
__init__
中设置实例属性并在实例方法中读取或更改它们是正常的。 That is also why you pass self
als the first argument to def Change
.这也是为什么您将
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
I used the following method to quickly convert a couple of Fortran codes to Python.我使用以下方法快速将几个 Fortran 代码转换为 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
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
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.当然,在实际代码中,您会在 dict 查找中添加错误检查。
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'
Since dictionaries are passed by reference, you can use a dict variable to store any referenced values inside it.由于字典是通过引用传递的,因此您可以使用 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!"
return result
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 returned value
print("multi: ", ref['multi']) # a referenced value
print("msg: ", ref['msg']) # a referenced value
Since it seems to be nowhere mentioned an approach to simulate references as known from eg C++ is to use an "update" function and pass that instead of the actual variable (or rather, "name"):由于似乎没有提到模拟引用的方法,例如 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
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.显然上面不允许读取值,只能更新它。
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.虽然通过引用传递并不适合 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.一种方法是在包装函数中使用
global
(用于全局变量)或非nonlocal
(用于函数中的局部变量)。
def change(wrapper):
wrapper(7)
x = 5
def setter(val):
global x
x = val
print(x)
The same idea works for reading and del
eting a variable.同样的想法适用于读取和
del
变量。
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.对于只是阅读,甚至还有一种更短的方法来使用
lambda: x
,它返回一个可调用的,当被调用时返回 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:传递 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.这里的
ByRef
类包装了一个字典访问。 So attribute access to wrapped
is translated to a item access in the passed dictionary.因此,对
wrapped
的属性访问被转换为传递字典中的项目访问。 By passing the result of the builtin locals
and the name of a local variable this ends up accessing a local variable.通过传递内置
locals
变量的结果和局部变量的名称,最终可以访问局部变量。 The python documentation as of 3.5 advises that changing the dictionary might not work but it seems to work for me.从 3.5 开始的 python 文档建议更改字典可能不起作用,但它似乎对我有用。
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)
Pass-By-Reference in Python is quite different from the concept of pass by reference in C++/Java. Python 中的引用传递与 C++/Java 中的引用传递概念完全不同。
I am new to Python, started yesterday (though I have been programming for 45 years).我是 Python 新手,从昨天开始(尽管我已经编程了 45 年)。
I came here because I was writing a function where I wanted to have two so called out-parameters.我来到这里是因为我正在编写一个函数,我想要两个所谓的输出参数。 If it would have been only one out-parameter, I wouldn't get hung up right now on checking how reference/value works in Python.
如果它只是一个输出参数,我现在不会挂断检查引用/值在 Python 中的工作方式。 I would just have used the return value of the function instead.
我只会使用函数的返回值。 But since I needed two such out-parameters I felt I needed to sort it out.
但是由于我需要两个这样的输出参数,所以我觉得我需要对其进行整理。
In this post I am going to show how I solved my situation.在这篇文章中,我将展示我是如何解决我的情况的。 Perhaps others coming here can find it valuable, even though it is not exactly an answer to the topic question.
也许来到这里的其他人会发现它很有价值,即使它并不完全是主题问题的答案。 Experienced Python programmers of course already know about the solution I used, but it was new to me.
经验丰富的 Python 程序员当然已经知道我使用的解决方案,但它对我来说是新的。
From the answers here I could quickly see that Python works a bit like Javascript in this regard, and that you need to use workarounds if you want the reference functionality.从这里的答案中,我可以很快看到 Python 在这方面的工作方式有点像 Javascript,如果你想要参考功能,你需要使用变通方法。
But then I found something neat in Python that I don't think I have seen in other languages before, namely that you can return more than one value from a function, in a simple comma separated way, like this:但是后来我在 Python 中发现了一些我认为我以前在其他语言中没有见过的简洁的东西,即你可以以简单的逗号分隔方式从函数返回多个值,如下所示:
def somefunction(p):
a=p+1
b=p+2
c=-p
return a, b, c
and that you can handle that on the calling side similarly, like this并且您可以类似地在调用方处理它,就像这样
x, y, z = somefunction(w)
That was good enough for me and I was satisfied.这对我来说已经足够好了,我很满意。 No need to use some workaround.
无需使用一些解决方法。
In other languages you can of course also return many values, but then usually in the from of an object, and you need to adjust the calling side accordingly.在其他语言中,您当然也可以返回许多值,但通常在对象的 from 中,您需要相应地调整调用方。
The Python way of doing it was nice and simple. Python 的做法很简单。
If you want to mimic by reference even more, you could do as follows:如果您想通过引用来模仿更多,您可以执行以下操作:
def somefunction(a, b, c):
a = a * 2
b = b + a
c = a * b * c
return a, b, c
x = 3
y = 5
z = 10
print(F"Before : {x}, {y}, {z}")
x, y, z = somefunction(x, y, z)
print(F"After : {x}, {y}, {z}")
which gives this result这给出了这个结果
Before : 3, 5, 10 After : 6, 11, 660
alternatively you could use ctypes witch would look something like this或者你可以使用 ctypes 女巫看起来像这样
import ctypes
def f(a):
a.value=2398 ## resign the value in a function
a = ctypes.c_int(0)
print("pre f", a)
f(a)
print("post f", a)
as a is ac int and not a python integer and apperently passed by reference.因为 a 是 ac int 而不是 python 整数,并且显然是通过引用传递的。 however you have to be carefull as strange things could happen and is therefor not advised
但是你必须小心,因为可能会发生奇怪的事情,因此不建议
Most likely not the most reliable method but this works, keep in mind that you are overloading the built-in str function which is typically something you don't want to do:很可能不是最可靠的方法,但这是可行的,请记住,您正在重载内置的 str 函数,这通常是您不想做的事情:
import builtins
class sstr(str):
def __str__(self):
if hasattr(self, 'changed'):
return self.changed
return self
def change(self, value):
self.changed = value
builtins.str = sstr
def change_the_value(val):
val.change('After')
val = str('Before')
print (val)
change_the_value(val)
print (val)
What about dataclasses ?数据类呢? Also, it allows you to apply type restriction (aka "type hint").
此外,它还允许您应用类型限制(又名“类型提示”)。
from dataclasses import dataclass
@dataclass
class Holder:
obj: your_type # Need any type? Use "obj: object" then.
def foo(ref: Holder):
ref.obj = do_something()
I agree with folks that in most cases you'd better consider not to use it.我同意人们在大多数情况下最好考虑不使用它的观点。
And yet, when we're talking about contexts it's worth to know that way.然而,当我们谈论上下文时,了解这种方式是值得的。
You can design explicit context class though.您可以设计显式上下文类。 When prototyping I prefer dataclasses, just because it's easy to serialize them back and forth.
在进行原型设计时,我更喜欢数据类,只是因为很容易来回序列化它们。
Cheers!干杯!
Python assigns a unique identifier to each object and this identifier can be found by using Python's built-in id()
function. Python 为每个对象分配一个唯一标识符,并且可以使用 Python 的内置
id()
函数找到该标识符。 It is ready to verify that actual and formal arguments in a function call have the same id value, which indicates that the dummy argument and actual argument refer to the same object.可以验证函数调用中的实际参数和形式参数是否具有相同的 id 值,这表明虚拟参数和实际参数引用的是同一个对象。
Note that the actual argument and the corresponding dummy argument are two names referring to the same object.请注意,实际参数和相应的虚拟参数是指同一个对象的两个名称。 If you re-bind a dummy argument to a new value/object in the function scope, this does not effect the fact that the actual argument still points to the original object because actual argument and dummy argument are two names.
如果您将虚拟参数重新绑定到函数范围内的新值/对象,这不会影响实际参数仍然指向原始对象的事实,因为实际参数和虚拟参数是两个名称。
The above two facts can be summarized as “arguments are passed by assignment”.上述两个事实可以概括为“参数通过赋值传递”。 ie,
IE,
dummy_argument = actual_argument
If you re-bind dummy_argument
to a new object in the function body, the actual_argument
still refers to the original object.如果将
dummy_argument
重新绑定到函数体中的新对象, actual_argument
仍然引用原始对象。 If you use dummy_argument[0] = some_thing
, then this will also modify actual_argument[0]
.如果您使用
dummy_argument[0] = some_thing
,那么这也会修改actual_argument[0]
。 Therefore the effect of “pass by reference” can be achieved by modifying the components/attributes of the object reference passed in. Of course, this requires that the object passed is a mutable object.因此通过修改传入的对象引用的组件/属性可以达到“通过引用传递”的效果。当然,这要求传递的对象是可变对象。
def i_my_wstring_length(wstring_input:str = "", i_length:int = 0) -> int:
i_length[0] = len(wstring_input)
return 0
wstring_test = "Test message with 32 characters."
i_length_test = [0]
i_my_wstring_length(wstring_test, i_length_test)
print("The string:\n\"{}\"\ncontains {} character(s).".format(wstring_test, *i_length_test))
input("\nPress ENTER key to continue . . . ")
Any variable in Python stores a value . Python 中的任何变量都存储一个值。
Assigning a variable to another, included a function argument, creates a copy of that value.将一个变量分配给另一个变量,包括一个函数参数,会创建该值的副本。
For a scalar variable, that value is just the value to retrieve (a number, string, etc).对于标量变量,该值只是要检索的值(数字、字符串等)。
For a list , that value is an address that references the location another value/s .对于list ,该 value 是引用另一个 value/s位置的地址。
There is also the global keyword , it extends the scope of a variable to the function where is applied.还有global 关键字,它将变量的范围扩展到应用的函数。
There is a python course that explains these concepts, and have also a sandbox to test the code, here are the relevant sections:有一个 Python 课程解释了这些概念,还有一个沙箱来测试代码,以下是相关部分:
This might be an elegant object oriented solution without this functionality in Python.这可能是一个优雅的面向对象的解决方案,在 Python 中没有此功能。 An even more elegant solution would be to have any class you make subclass from this.
一个更优雅的解决方案是让任何你从中创建子类的类。 Or you could name it "MasterClass".
或者您可以将其命名为“MasterClass”。 But instead of having a single variable and a single boolean, make them a collection of some kind.
但不是只有一个变量和一个布尔值,而是让它们成为某种集合。 I fixed the naming of your instance variables to comply with PEP 8.
我修复了实例变量的命名以符合 PEP 8。
class PassByReference:
def __init__(self, variable, pass_by_reference=True):
self._variable_original = 'Original'
self._variable = variable
self._pass_by_reference = pass_by_reference # False => pass_by_value
self.change(self.variable)
print(self)
def __str__(self):
print(self.get_variable())
def get_variable(self):
if pass_by_reference == True:
return self._variable
else:
return self._variable_original
def set_variable(self, something):
self._variable = something
def change(self, var):
self.set_variable(var)
def caller_method():
pbr = PassByReference(variable='Changed') # this will print 'Changed'
variable = pbr.get_variable() # this will assign value 'Changed'
pbr2 = PassByReference(variable='Changed', pass_by_reference=False) # this will print 'Original'
variable2 = pbr2.get_variable() # this will assign value 'Original'
I solved a similar requirement as follows:我解决了一个类似的要求,如下所示:
To implement a member function that changes a variable, dont pass the variable itself, but pass a functools.partial
that contains setattr
referring to the variable.要实现更改变量的成员函数,请不要传递变量本身,而是传递包含引用变量的
setattr
的functools.partial
。 Calling the functools.partial
inside change()
will execute settatr
and change the actual referenced variable.在
change()
中调用functools.partial
将执行settatr
并更改实际引用的变量。
Note that setattr
needs the name of the variable as string.请注意,
setattr
需要将变量的名称作为字符串。
class PassByReference(object):
def __init__(self):
self.variable = "Original"
print(self.variable)
self.change(partial(setattr,self,"variable"))
print(self.variable)
def change(self, setter):
setter("Changed")
Most of the time, the variable to be passed by reference is a class member.大多数时候,通过引用传递的变量是类成员。 The solution I suggest is to use a decorator to add both a field that is mutable and corresponding property.
我建议的解决方案是使用装饰器来添加可变字段和相应属性。 The field is a class wrapper around the variable.
该字段是围绕变量的类包装器。
The @refproperty
adds both self._myvar
(mutable) and self.myvar
property. @refproperty
添加了self._myvar
(可变)和self.myvar
属性。
@refproperty('myvar')
class T():
pass
def f(x):
x.value=6
y=T()
y.myvar=3
f(y._myvar)
print(y.myvar)
It will print 6.它将打印 6。
Compare this to:将此与以下内容进行比较:
class X:
pass
x=X()
x.myvar=4
def f(y):
y=6
f(x.myvar)
print(x.myvar)
In this case, it won't work.在这种情况下,它不起作用。 It will print 4.
它将打印 4。
The code is the following:代码如下:
def refproperty(var,value=None):
def getp(self):
return getattr(self,'_'+var).get(self)
def setp(self,v):
return getattr(self,'_'+var).set(self,v)
def decorator(klass):
orginit=klass.__init__
setattr(klass,var,property(getp,setp))
def newinit(self,*args,**kw):
rv=RefVar(value)
setattr(self,'_'+var,rv)
orginit(self,*args,**kw)
klass.__init__=newinit
return klass
return decorator
class RefVar(object):
def __init__(self, value=None):
self.value = value
def get(self,*args):
return self.value
def set(self,main, value):
self.value = value
Simple Answer:简单的答案:
In python like c++, when you create an object instance and pass it as a parameter, no copies of the instance itself get made, so you are referencing the same instance from outside and inside the function and are able to modify the component datums of the same object instance,hence changes are visible to the outside.在像 c++ 这样的 python 中,当您创建一个对象实例并将其作为参数传递时,不会生成实例本身的副本,因此您可以从函数外部和内部引用同一个实例,并且能够修改组件的基准相同的对象实例,因此外部可见更改。
For basic types, python and c++ also behave the same to each other, in that copies of the instances are now made, so the outside sees/modifies a different instance than the inside of the function.对于基本类型,python 和 c++ 的行为也彼此相同,因为现在制作了实例的副本,因此外部看到/修改了与函数内部不同的实例。 Hence changes from the inside are not visible on the outside.
因此,内部的变化在外部是不可见的。
Here comes the real difference between python and c++:这是python和c ++之间的真正区别:
c++ has the concept of address pointers, and c++ allows you to pass pointers instead, which bypasses the copying of basic types, so that the inside of the function can affect the same instances as those outside, so that the changes are also visible to the outside. c++有地址指针的概念,c++允许你改为传递指针,绕过了基本类型的复制,这样函数内部就可以影响到和外部一样的实例,这样变化对函数也是可见的外部。 This has no equivalent in python, so is not possible without workarounds (such as creating wrapper types).
这在 python 中没有等价物,因此如果没有变通方法(例如创建包装器类型)是不可能的。
Such pointers can be useful in python, but it's not as necessary as it is in c++, because in c++, you can only return a single entity, whereas in python you can return multiple values separated by commas (ie a tuple).这样的指针在 python 中可能很有用,但它不像在 c++ 中那样必要,因为在 c++ 中,您只能返回单个实体,而在 python 中,您可以返回由逗号分隔的多个值(即元组)。 So in python, if you have variables a,b, and c, and want a function to modify them persistently (relative to the outside), you would do this:
所以在 python 中,如果你有变量 a、b 和 c,并且想要一个函数来持久地修改它们(相对于外部),你可以这样做:
a=4
b=3
c=8
a,b,c=somefunc(a,b,c)
# a,b,c now have different values here
Such syntax is not easily possible in c++, thus in c++ you would do this instead:这样的语法在 c++ 中不容易实现,因此在 c++ 中你可以这样做:
int a=4
int b=3
int c=8
somefunc(&a,&b,&c)
// a,b,c now have different values here
I share another fun way for people to comprehend this topic over a handy tool - VISUALIZE PYTHON CODE EXECUTION based on the example of passing a mutable list from @Mark Ransom我分享了另一种有趣的方式让人们通过一个方便的工具来理解这个主题 - 基于传递来自@Mark Ransom 的可变列表的示例,可视化 PYTHON 代码执行
Just play it around, and then you will figure it out.随便玩玩,然后你就知道了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.