在Python范围内,函数/方法中的参数是否通过值或引用传递?
我已经进行了一些研究,并且有很好的资源:Are integers in Python passed by value or reference?
这个详细的参考可能是非常好的参考,但是主要是关于整数的。 ti如何表示功能/方法的参数
我在命令行中做了一些测试/跟踪:
his_list = [1, 2, 3]
her_list = [4, 5, 6]
def change_list1(mylist):
mylist.append(100)
return mylist
def change_list2(mylist):
mylist = her_list
return mylist
test_list1 = change_list1(his_list)
test_list2 = change_list2(his_list)
print('test_list1: {}'.format(test_list1))
print('test_list2: {}'.format(test_list2))
print('his_list: {}'.format(his_list))
test_string = change_string(his_string)
print('test_string: {}'.format(test_string))
print('his_string: {}'.format(his_string))
结果是:
test_list1: [1, 2, 3, 100]
test_list2: [4, 5, 6]
his_list: [1, 2, 3, 100]
test_string: abcdef
his_string: abc
如果参数是通过引用传递的,为什么change_list2的结果看起来像是按值传递的?另外,当我将代码放入IDE中时,change_list2中的参数更改为灰色(暗示未使用其值)?
答案 0 :(得分:2)
Python通过 object 传递。在change_list1
中,传递一个可变对象,然后对其进行突变。它是同一对象(一个列表),只是被更改(内容已更改)。 mylist
是引用该对象的局部参数。该函数返回原始对象。您还会发现his_list
也被更改了,因为它引用了相同的对象。
在change_list2
中,您还传递了一个可变对象,但没有对它进行突变,而是将一个新对象分配给局部参数。原始对象未更改。该函数返回新对象。
将Python中的变量视为对象名称很有帮助。将参数传递给函数时,函数中的参数名称只是传递给函数的原始对象的新名称。
您可以通过打印传递到函数中的对象名称的ID和参数的对象名称的ID来查看此信息。
a = [1,2,3] # mutable object (content can be altered)
b = 1 # immutable object (content can't be altered).
print(f'id(a) = {id(a)}, id(b) = {id(b)}')
def func(c,d):
print('INSIDE func')
print(f'id(c) = {id(c)}, id(d) = {id(d)}')
c.append(4) # mutable object can be altered.
d = 4 # local name 'd' assigned a new object.
print('AFTER altering c & d')
print(f'id(c) = {id(c)}, id(d) = {id(d)}')
return c,d
e,f = func(a,b)
print('AFTER func called')
print(f'id(a) = {id(a)}, id(b) = {id(b)}')
print(f'id(e) = {id(e)}, id(f) = {id(f)}')
print(f'a = {a}, b = {b}')
print(f'e = {e}, f = {f}')
输出:
id(a) = 2290226434440, id(b) = 1400925216
INSIDE func
id(c) = 2290226434440, id(d) = 1400925216
AFTER altering c & d
id(c) = 2290226434440, id(d) = 1400925312
AFTER func called
id(a) = 2290226434440, id(b) = 1400925216
id(e) = 2290226434440, id(f) = 1400925312
a = [1, 2, 3, 4], b = 1
e = [1, 2, 3, 4], f = 4
请注意,a
和b
的ID与参数名称c
和d
一致。这些名称指的是相同的对象。
c
对象被突变。 a
指向相同的对象,因此将显示相同的更改。
d
对象是不可变的。没有像.append
这样的方法可以更改对象的内容。 d
只能重新分配。它被重新分配并显示一个新的ID。 b
对象不变。
函数调用后,e
成为名为c
(也称为a
)的对象的新名称。名称c
不再存在。这是一个本地函数名称。 f
成为对象d
(整数4)的新名称,并且d
不再存在。 b
不受影响,并引用原始整数1。
a
和e
指的是原始但变异的对象。 b
和f
指的是不同的,不变的对象。
答案 1 :(得分:2)
Python使用一种机制,称为“按对象调用”,有时也称为“按对象引用调用”或“按共享调用”。
如果将不可变的参数(例如整数,字符串或元组)传递给函数,则传递的行为类似于按值调用。对象引用传递给函数参数。它们不能在函数中更改,因为它们根本无法更改,即它们是不可变的。如果传递可变参数,则有所不同。它们也通过对象引用传递,但是可以在函数中对其进行更改。如果我们将列表传递给函数,则必须考虑两种情况:列表元素可以就地更改,即,即使在调用者的范围内,列表也将更改。如果将新列表分配给该名称,则旧列表将不受影响,即,呼叫者范围内的列表将保持不变。