Python变量范围(通过引用或复制传递?)

Python变量范围(通过引用或复制传递?),python,scope,Python,Scope,为什么在排序(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 testScop

为什么在
排序(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]
参照与单独的对象不同


.sort()
也会变异集合。

文档中特别提到了
.sort()
函数会变异集合。如果要在已排序的集合上迭代,请使用
sorted(L)
。这提供了一个生成器,而不仅仅是对列表进行排序。

长话短说:Python使用按值传递,但按值传递的是引用。实际对象有0到无穷多个指向它们的引用,为了改变该对象,您是谁以及如何获得对该对象的引用并不重要

逐步浏览您的示例:

  • L=[…]
    在内存中的某处创建一个
    列表
    对象,局部变量
    L
    存储对该对象的引用
  • 排序
    (严格来说,指向全局名称的可调用对象
    排序
    )通过
    L
    存储的引用副本进行调用,并将其存储在名为
    x
    的本地文件中
  • 调用
    x
    中包含的引用指向的对象的方法
    sort
    。它还获取对象的引用(在
    self
    参数中)。它以某种方式改变了那个对象(对象,而不是对对象的引用,它仅仅是一个内存地址)
  • 现在,由于引用是复制的,但不是引用指向的对象,所以我们讨论的所有其他引用仍然指向同一个对象。“就地”修改的一个对象
  • testScope
    然后返回对该列表对象的另一个引用
  • print
    使用它请求字符串表示(调用
    \uuuu str\uuu
    方法)并输出它。因为它仍然是同一个对象,当然它正在打印排序列表
因此,无论何时你将一个物体传递到任何地方,你都会与接收它的人分享它。函数可以(但通常不会)改变它们传递的对象(由引用指向),从调用改变方法到分配成员。请注意,分配一个成员不同于分配一个普通的ol'名称,这仅仅意味着改变您的本地范围,而不是调用方的任何对象。因此,您不能变异调用方的局部变量(这就是为什么它不是按引用传递)


进一步阅读:为什么它不是按引用传递,也不是大多数人所说的按值传递。

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[:]

我知道sort()会改变集合,但为什么它会改变testScope()中定义的原始L。听起来排序(L)是将L的引用传递给排序函数?对吗?这是正确的说法吗?@lunixbochs:不,不,不!请停止暗示Python、Java、C#(使用引用类型时)等语言。请使用按引用传递。这是完全错误的。那么,你是说内存“1”中的对象仍然存在,并且仍然由变量b指向?只有可变对象可以变异,而不是全部。一个字符串是。您只能更改变量,使其名称引用不同的对象,但不能更改不可变对象本身。不可变对象没有可以更改其值的方法。一个列表被称为可变对象,这意味着它有可以改变其值的方法或操作。名称绑定与Python中的易变性是完全不同的。@naxa关于这个答案,您的观点是什么?绑定名称确实与变异对象完全不同,答案也是如此。它之所以提到这两者,是为了指出它们是不同的。指出这一点的原因是,几乎所有初学者对参数传递和易变性的困惑都来自于他们没有意识到这一区别。对不起!关键是:这里有一句错误或误导性的话:
实际对象有0到无穷多个引用,每个引用的人都可以对它们进行变异。
因为你不能仅通过引用对象来变异对象。它需要是可变的。我认为让初学者理解比正确更重要,但我也认为给出错误的假设是一个同样糟糕的主意。这件事很难解决。但它最好是固定的。如果我有其他选择,我应该让你知道。关:你可以删除我们之前的评论来保持它的整洁吗?@naxa我明白了。但我想不出比这更好的方法了。有人可能会说“试图变异”,但这就提出了一个问题,即它是否会失败取决于引用的来源。我们可以将不可变对象作为特例豁免,但这似乎是荒谬的“语言对不可变对象的处理方式不同”。我倾向于离开它,将其视为空洞的事实:所有变异不可变对象(即无)的代码都可以这样做,无论它从何处获得对该对象的引用。我认为这比我能想到的所有备选方案都要少一些误导。@naxa实际上我认为我找到了一个更好的措辞,看看吧。
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