Python 变量作用域(引用传递还是复制?)
为什么在调用 sorting(L)
函数时,变量 L 会被改变呢?在其他编程语言中,通常会把 L 的一个副本传递给 sorting()
,这样对 x
的任何修改都不会影响到原来的变量?
def sorting(x):
A = x #Passed by reference?
A.sort()
def testScope():
L = [5,4,3,2,1]
sorting(L) #Passed by reference?
return L
>>> print testScope()
>>> [1, 2, 3, 4, 5]
4 个回答
1
文档中特别提到,.sort()
这个函数会直接改变原来的集合。如果你想遍历一个已经排序好的集合,可以使用sorted(L)
。这个方法会提供一个生成器,而不是仅仅对列表进行排序。
11
Python中有可变对象和不可变对象的概念。像字符串或整数这样的对象是不可变的——你每次对它们的修改都会生成一个新的字符串或整数。
而列表是可变的,可以直接在原地进行修改。下面的例子可以看看。
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print a is b, a is c
# False True
print a, b, c
# [1, 2, 3] [1, 2, 3] [1, 2, 3]
a.reverse()
print a, b, c
# [3, 2, 1] [1, 2, 3] [3, 2, 1]
print a is b, a is c
# False True
注意到c被反转了,因为c“是”a的一个副本。其实有很多方法可以把一个列表复制到内存中的新对象。一个简单的方法是切片:c = a[:]
21
简单来说:Python 是通过值传递的,但传递的值其实是引用。实际的对象可以有从零到无数个引用指向它,而在修改这个对象时,谁拥有这个引用并不重要。
接下来一步一步分析你的例子:
L = [...]
这行代码在内存中创建了一个list
对象,局部变量L
存储了指向这个对象的引用。sorting
(严格来说,是指向全局名称sorting
的可调用对象)被调用时,传入的是L
存储的引用的一个副本,并把它存储在一个叫x
的局部变量中。- 接着,
x
中引用的对象的sort
方法被调用。这个方法也得到了指向对象的引用(在self
参数中)。它以某种方式修改了这个对象(是这个对象本身,而不是指向它的引用,后者只是一个内存地址)。 - 现在,由于引用是被复制的,但指向的对象并没有被复制,所以我们讨论的其他引用仍然指向 同一个 对象。这个对象是在原地被修改的。
testScope
然后返回另一个指向这个列表对象的引用。print
使用这个引用请求字符串表示(调用__str__
方法)并输出它。因为它仍然是同一个对象,所以当然会打印出排序后的列表。
所以每当你传递一个对象时,你就是在和接收它的人 共享 这个对象。函数可以(但通常不会)修改它们接收到的对象(通过调用修改方法或赋值成员)。不过要注意,赋值一个成员和赋值一个普通的名字是不同的——赋值一个名字只是修改了你的局部作用域,并不会影响调用者的对象。所以你不能修改调用者的局部变量(这就是为什么它不是通过引用传递的原因)。
进一步阅读:effbot.org 上关于为什么这不是通过引用传递的讨论,这与大多数人所说的通过值传递也不同。