代码
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = number + 2
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = 15
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable
main()
当我读它的时候,我以为它会输出4 8 12 15
,但它输出4 13 12 15
。我可以在这里看到Python处理整数和列表的方式不同,我认为如果没有全局,最后一件事是不可能的。我无法理解输出,在这种情况下,为什么它不输出4 13 12 17
?
你可以在这里看到几乎相同的代码,不同的类型和不同的参考:
$ python test2.py
4
13
12
15
$ python test3.py
4
13
12
17
$ cat test2.py test3.py
通过参考示例
test2.py:通过引用和可变数据类型传递-示例。表/列表不足以影响main中的局部变量,您需要引用!
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = [x+2 for x in number]
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = [15]
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
test3.py:通过引用的例子,在主函数之外更改可变数据类型列表/表
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number[0] += 2
def main():
numbers = [4, 8, 12]
change1(numbers)
variable = [15]
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
传递值示例
test4.py:试图找到一个具有pass-by-value的示例,为什么它不起作用?
$ cat test4.py
# Not yet a pass-by-value example!
global variable
variable = [15]
def change1(list1):
list1[1] = list1[1] + 5
def change2(number):
number = [x+2 for x in number]
def main():
numbers = [4, 8, 12]
change1(numbers)
#variable = 15
change2(variable)
i = 0
while i < 3:
print numbers[i]
i += 1
print variable[0]
main()
$ python test4.py
4
13
12
15 # I expected 17! Why no 17?
def change1(list1):
# `list1[1] =` means you are changing the object passed in
list1[1] = list1[1] + 5
def change2(number):
# `number = ` means you create a **new local variable**, number,
# based on the `number`you passed in
number = [x+2 for x in number]
因此,如果你想更改现有的对象,你必须以某种方式引用它们,例如在中
def change3(number):
# `number[:]` is the whole existing list and you overwrite it
number[:] = [x+2 for x in number]
更改列表时请注意[ .. ]
。
Python参数是通过引用传递的。您在change1
中只更改了一个对象。
但是,数值和字符串都是不可变的。您不能更改传入的不可变的值,也不能在调用者中看到该值的更改。另一方面,Dictionaries和Lists是可变的,当函数返回时,被调用函数对它们所做的更改将被保留。
更多信息:http://www.penzilla.net/tutorials/python/functions/
最终的答案是Python实际上是"通过共享调用",也称为"通过对象调用"或"通过对象引用调用"。
这在以前已经被广泛讨论过。来自那篇文章:
有时,那些读过一点CS但没有读过很多CS(或者只读过太多一种CS(的人会出现在comp.lang.python上,浪费大量精力试图告诉每个人python正在使用一些它并没有真正使用的调用模型。事实证明,他们并不真正理解Python的模型,而且往往也不理解他们最喜欢的模型。
但别介意,你唯一需要知道的是,Python的模型既不是"按值调用",也不是"按引用调用"(因为任何为Python使用这些术语的尝试都需要使用单词"-value"one_answers"-reference"的非标准定义(。最准确的描述是CLU的">通过对象调用"或">通过共享调用"。或者,如果您愿意,"<strong]通过对象引用调用>
如果你还没有读过这篇文章,你也应该读一下。
Python的语义与CLU语言的语义最为相似。Liskov等人的CLU参考手册描述了如下语义:
"我们通过共享调用参数传递技术,因为参数对象在调用者和被调用例程。此技术不会与大多数传统的论点传递技术相对应(类似于LISP中的参数传递(尤其是不是按值调用的,因为-由被调用例程形成的将对调用者可见。并且它不是通过引用调用的,因为没有提供访问权限调用程序的变量,但仅限于某些对象。">
在change1
中,您可以将列表中的值与value + 5
交换
在change2
中,将5添加到number
。结果是一个新对象,而不仅仅应用于传递的变量。如果你来自C++:不,Python中就没有int& var
。
这样做会得到预期的结果:
def change2(number):
return number + 5
variable = 15
variable = change2(variable)
如果您仍然不想返回值,可以创建一个MutableInt
类。
class MutableInt(object):
def __init__(self, value = 0):
self._value = int(value)
def __add__(self, other):
self._value += int(other)
return self
def __sub__(self, other):
self._value -= int(other)
return self
...
所有示例都显示了按值调用。Python只有按值调用。没有引用调用。python中的所有值都是引用(不可能有一个"对象"作为值(。因此,在传递给函数时会复制引用。列表是可变的,因此可以通过共享引用来更改其内容。在change2中,您将重新分配一个局部变量以指向另一个对象,这与所有对局部变量的赋值一样,对任何调用范围都没有影响,因为它是按值调用的。