Python 为什么函数可以修改调用者感知到的某些参数,而不能修改其他参数?
我试图理解Python处理变量范围的方法。在本例中,为什么Python 为什么函数可以修改调用者感知到的某些参数,而不能修改其他参数?,python,Python,我试图理解Python处理变量范围的方法。在本例中,为什么f()能够改变x的值,就像在main()中看到的那样,而不是n的值 def f(n, x): n = 2 x.append(4) print('In f():', n, x) def main(): n = 1 x = [0,1,2,3] print('Before:', n, x) f(n, x) print('After: ', n, x) main() 输出: B
f()
能够改变x
的值,就像在main()
中看到的那样,而不是n
的值
def f(n, x):
n = 2
x.append(4)
print('In f():', n, x)
def main():
n = 1
x = [0,1,2,3]
print('Before:', n, x)
f(n, x)
print('After: ', n, x)
main()
输出:
Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After: 1 [0, 1, 2, 3, 4]
f
实际上并不改变x
的值(它始终是对列表实例的相同引用)。相反,它会改变此列表的内容
在这两种情况下,引用的副本都会传递给函数。在函数内部
获得一个新值。只修改函数内部的引用,而不修改函数外部的引用n
不会被分配新的值:函数内部和外部的引用都不会被修改。相反,x
的值被修改x
由于函数内部和外部的
x
都引用相同的值,因此都可以看到修改。相比之下,函数内部和外部的n
在函数内部重新分配n
后引用了不同的值。这是因为列表是一个可变对象。您没有将x设置为[0,1,2,3]的值,而是为对象[0,1,2,3]定义了一个标签
您应该像这样声明函数f():
def f(n, x=None):
if x is None:
x = []
...
n是一个int(不可变),并且一个副本被传递给函数,因此在函数中您正在更改副本
X是一个列表(可变),指针的副本被传递给函数,因此X.append(4)更改列表的内容。但是,您在函数中说过x=[0,1,2,3,4],您不会更改main()中x的内容。我将重命名变量以减少混淆。n->nf或nmain。x->xf或xmain: 当调用函数f时,Python运行库生成一个xmain的副本并将其分配给xf,类似地,还将nmain的副本分配给nf
在n的情况下,复制的值为1 对于x,复制的值不是文本列表[0,1,2,3]。它是对该列表的引用。xf和xmain指向同一个列表,因此当您修改xf时,您也在修改xmain 然而,如果你要写的东西是:
xf = ["foo", "bar"]
xf.append(4)
你会发现xmain没有改变。这是因为,在xf=[“foo”,“bar”]行中,您必须将xf更改为指向一个新的列表。对这个新列表所做的任何更改都不会对xmain仍然指向的列表产生影响
希望有帮助。:-) 有些答案在函数调用的上下文中包含“copy”一词。我觉得很困惑 Python永远不会复制在函数调用期间传递的对象。 函数参数是名称。调用函数时,Python会将这些参数绑定到您传递的任何对象(通过调用方作用域中的名称) 对象可以是可变的(如列表)或不可变的(如Python中的整数、字符串)。可以更改的可变对象。不能更改名称,只需将其绑定到另一个对象即可 您的示例不是关于,而是关于Python的
def f(n, x): # these `n`, `x` have nothing to do with `n` and `x` from main()
n = 2 # put `n` label on `2` balloon
x.append(4) # call `append` method of whatever object `x` is referring to.
print('In f():', n, x)
x = [] # put `x` label on `[]` ballon
# x = [] has no effect on the original list that is passed into the function
下面是一些漂亮的图片。你已经有了很多答案,我大体上同意J.F.Sebastian的观点,但你可能会发现这是一条有用的捷径: 每当您看到
varname=
时,您都在函数范围内创建一个新的名称绑定。在此范围内,以前绑定到varname
的任何值都将丢失
任何时候看到varname.foo()
都是在varname
上调用方法。该方法可能会更改varname(例如,list.append
)varname
(或者说,varname
名称的对象)可能存在于多个作用域中,并且由于它是同一个对象,因此所有作用域中的任何更改都是可见的
[注意,
global
关键字为第一种情况创建了一个异常]如果您正确考虑Python,它是一种纯粹的传递值语言。python变量在内存中存储对象的位置。Python变量不存储对象本身。将变量传递给函数时,传递的是该变量指向的对象地址的副本
对比这两个函数
def foo(x):
x[0] = 5
def goo(x):
x = []
现在,当您在shell中键入
>>> cow = [3,4,5]
>>> foo(cow)
>>> cow
[5,4,5]
把这个和goo比较一下
>>> cow = [3,4,5]
>>> goo(cow)
>>> goo
[3,4,5]
在第一种情况下,我们将cow地址的副本传递给foo,foo修改了驻留在那里的对象的状态。对象被修改
在第二种情况下,您将cow地址的副本传递给goo。然后顾继续更改该副本。效果:无
我称之为粉红屋原则。如果你把你的地址复印一份,然后告诉
油漆工把那个地址的房子漆成粉红色,你会得到一栋粉红色的房子。
如果你给画家一份你的地址并告诉他换一个新地址,
你家的地址不变
这个解释消除了许多混乱。Python通过值传递存储的地址变量 Python是按引用值复制的。对象在内存中占据一个字段,引用与该对象关联,但它本身在内存中占据一个字段。名称/值与引用关联。在python函数中,它总是复制引用的值,因此在代码中,n被复制为一个新名称,当您分配该名称时,它在调用方堆栈中有一个新的空间。但是对于列表,名称也被复制了,但是它引用了相同的内存(因为您从未为列表分配新值)。这是python中的一种魔力 如果函数是用完全不同的变量重新编写的,我们调用它们,那么它就很好地说明了这一点。我一开始并没有明白这一点,所以我试着理解/说服自己:
def f(y, z):
y = 2
z.append(4)
print ('In f(): ', id(y), id(z))
def main():
n = 1
x = [0,1,2,3]
print ('Before in main:', n, x,id(n),id(x))
f(n, x)
print ('After in main:', n, x,id(n),id(x))
main()
Before in main: 1 [0, 1, 2, 3] 94635800628352 139808499830024
In f(): 94635800628384 139808499830024
After in main: 1 [0, 1, 2, 3, 4] 94635800628352 139808499830024
z和x有相同的id,只有di
def f(y, z):
y = 2
z.append(4)
print ('In f(): ', id(y), id(z))
def main():
n = 1
x = [0,1,2,3]
print ('Before in main:', n, x,id(n),id(x))
f(n, x)
print ('After in main:', n, x,id(n),id(x))
main()
Before in main: 1 [0, 1, 2, 3] 94635800628352 139808499830024
In f(): 94635800628384 139808499830024
After in main: 1 [0, 1, 2, 3, 4] 94635800628352 139808499830024
def func1(x, l1):
x = 5
l1.append("nonsense")
y = 10
list1 = ["meaning"]
func1(y, list1)
print(y)
print(list1)
a = 'string'
a.replace('t', '_')
print(a)
>>> 'string'
a = a.replace('t', '_')
print(a)
>>> 's_ring'
b = 100
b + 1
print(b)
>>> 100
b = b + 1
print(b)
>>> 101