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

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


当前回答

正如jouell所说。这是一个什么指向什么的问题,我想补充的是,这也是一个=做什么和.append方法做什么之间的区别的问题。

When you define n and x in main, you tell them to point at 2 objects, namely 1 and [1,2,3]. That is what = does : it tells what your variable should point to. When you call the function f(n,x), you tell two new local variables nf and xf to point at the same two objects as n and x. When you use "something"="anything_new", you change what "something" points to. When you use .append, you change the object itself. Somehow, even though you gave them the same names, n in the main() and the n in f() are not the same entity, they only originally point to the same object (same goes for x actually). A change to what one of them points to won't affect the other. However, if you instead make a change to the object itself, that will affect both variables as they both point to this same, now modified, object.

让我们在不定义新函数的情况下说明.append方法和=方法之间的区别:

比较

    m = [1,2,3]
    n = m   # this tells n to point at the same object as m does at the moment
    m = [1,2,3,4] # writing m = m + [4] would also do the same
    print('n = ', n,'m = ',m)

to

    m = [1,2,3]
    n = m
    m.append(4)
    print('n = ', n,'m = ',m)

在第一个代码中,它将打印n = [1,2,3] m =[1,2,3,4],因为在第三行,你没有改变对象[1,2,3],而是你告诉m指向一个新的,不同的对象(使用'='),而n仍然指向原始对象。

在第二段代码中,它将输出n = [1,2,3,4] m =[1,2,3,4]。这是因为在整个代码中m和n仍然指向同一个对象,但是您使用.append方法修改了对象本身(m所指向的对象)…注意,不管你在第三行写m.p append(4)还是n.p append(4),第二段代码的结果都是一样的。

一旦你理解了这一点,剩下的唯一困惑就是真正理解,正如我所说,f()函数中的n和x与main()中的n和x并不相同,它们只是在调用f()时最初指向同一个对象。

其他回答

Python is copy by value of reference. An object occupies a field in memory, and a reference is associated with that object, but itself occupies a field in memory. And name/value is associated with a reference. In python function, it always copy the value of the reference, so in your code, n is copied to be a new name, when you assign that, it has a new space in caller stack. But for the list, the name also got copied, but it refer to the same memory(since you never assign the list a new value). That is a magic in 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的内容。

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

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

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

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

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