我试图理解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()

输出:

Before: 1 [0, 1, 2, 3]
In f(): 2 [0, 1, 2, 3, 4]
After:  1 [0, 1, 2, 3, 4]

请参见:如何通过引用传递变量?


当前回答

N是一个int(不可变),并且一个副本被传递给函数,因此在函数中您正在更改副本。

X是一个列表(可变),指针的副本被传递给函数,因此x.a pend(4)改变了列表的内容。然而,你在你的函数中说x =[0,1,2,3,4],你不会在main()中改变x的内容。

其他回答

我的一般理解是,任何对象变量(如列表或字典等)都可以通过其函数进行修改。我相信你不能做的是重新分配形参-即,在可调用函数中通过引用分配它。

这与许多其他语言是一致的。

运行下面的简短脚本,看看它是如何工作的:

def func1(x, l1):
    x = 5
    l1.append("nonsense")

y = 10
list1 = ["meaning"]
func1(y, list1)
print(y)
print(list1)

如果你正确地思考,Python是一种纯粹的值传递语言。python变量在内存中存储对象的位置。Python变量不存储对象本身。当您将变量传递给函数时,您正在传递变量所指向的对象的地址的副本。

对比这两个函数

def foo(x):
    x[0] = 5

def goo(x):
    x = []

现在,当你输入外壳的时候

>>> 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。然后goo继续更改该副本。效果:没有。

我称之为粉红房子原则。如果你把你的地址复印一份,并告诉a 如果油漆工把那个地址的房子漆成粉红色,你就会得到一座粉红色的房子。 如果你给油漆工一份你的地址复印件,让他把它改成一个新地址, 你家的地址不变。

这种解释消除了许多困惑。Python将地址变量按值存储。

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.a pend(4)改变了列表的内容。然而,你在你的函数中说x =[0,1,2,3,4],你不会在main()中改变x的内容。