Python:为什么引用列表的变量的范围不同于引用任何其他数据结构或数据类型的变量?
我发现引用列表的变量的范围不同于引用元组、整数或字符串的变量。为什么会这样 1) 当我使用整数、字符串或元组时:-Python:为什么引用列表的变量的范围不同于引用任何其他数据结构或数据类型的变量?,python,python-2.7,python-3.x,Python,Python 2.7,Python 3.x,我发现引用列表的变量的范围不同于引用元组、整数或字符串的变量。为什么会这样 1) 当我使用整数、字符串或元组时:- >>> def foo(anInt, aStr, aTup): anInt += 5 aStr += ' hi' aTup += (12,) print (anInt,aStr,aTup) >>> anInt, aStr, aTup = 5, 'Fox', (11,) &
>>> def foo(anInt, aStr, aTup):
anInt += 5
aStr += ' hi'
aTup += (12,)
print (anInt,aStr,aTup)
>>> anInt, aStr, aTup = 5, 'Fox', (11,)
>>> foo(anInt, aStr, aTup)
10 Fox hi (11, 12)
>>> print (anInt, aStr, aTup)
5 Fox (11,)
2) 当我使用列表时:-
>>> def foo(aList):
aList.append(2)
print (aList)
>>> aList = [1]
>>> foo(aList)
[1, 2]
>>> print (aList)
[1, 2]
在第一种情况下,anInt、aStr、aTup值的变化仅限于功能范围,而在列表范围变化的情况下
区别在于不可变类型int、string和tuple是按值传递的。当函数更新该值时,它正在更新本地副本
列表是通过引用传递的。使用append时,它正在更新原始列表。这不是范围问题。范围在不同类型之间没有差异。事实上,这并没有什么不同:变量只是一个对值的命名引用,完全与类型无关 但是Python中的某些类型是可变的(一旦创建,值就可以更改),而
list
就是其中之一。有些是不可变的,更改这些类型的值需要创建一个新对象<代码>+=以两种不同的方式用于可变和不可变类型。对于不可变类型,a+=b
相当于a=a+b
。创建了一个新对象,A
变量现在引用它。但是可变类型的对象是“就地”修改的,就像您的列表一样。你可能想读书
现在让我们看一下作用域。这里有传递给函数的全局对象。函数使用它们作为参数,而不是全局对象(是的,函数内部的aList
变量与函数外部的aList
变量不同,更多的是关于作用域的信息)。它使用对相同对象的其他引用,不能修改变量,只能修改变量引用的对象。但只有可变对象
如果比较以下两个代码示例的结果,您可能会感到惊讶
>>> a = 1; a1 = a; a1 += 1; a
1
>>> a = [1]; a1 = a; a1 += [1]; a
[1, 1]
有什么区别?唯一的区别是,
int
是一个不可变的类型,列表
是一个可变的类型。将a
分配给a1
后,它们总是引用单个对象。但是+=
操作符会在int
的情况下创建一个新对象。这里是一个很好的例子,说明了当使用一个变量引用int时,与列表相比,范围上的差异。
请注意“prev”变量:
1 class Solution:
2 def convertBST(self, root: TreeNode) -> TreeNode:
3
4 if root == None:
5 return root
6
7 prev = 0
8 def traverse(node):
9 if node.right:
10 traverse(node.right)
11 node.val += prev
12 prev = node.val
13 if node.left:
14 traverse(node.left)
15
16 traverse(root)
17 return root
此错误包含以下消息:
UnboundLocalError: local variable 'prev' referenced before assignment
node.val += prev
如果我用以下代码替换这些行,则不会出现错误:
第7行:prev=[0]
第11行:node.val+=prev[0]
第12行:prev[0]=node.val
让人们相信prev=0
在遍历函数中是不可见的,而prev=[0]
是可见的
但事实上,prev=0
也是可见的,并且可以在遍历函数中使用,如果没有对其进行赋值,即第11行出现UnboundLocalError
,仅当第12行存在时。
由于int的不变性,第12行导致遍历函数中的变量prev
指向新的内存位置,然后“隐藏”外部定义的变量prev=0
,从而导致错误
但是当一个新的int被分配给可变列表中的元素
prev[0]
时,列表中第一个元素的指针可以更新为指向新的int(遍历函数内部和外部);没有创建单独的作用域(即局部变量),原始的prev
变量在遍历函数中保持可见,并且prev[0]
可以在第12行赋值之前在第11行使用。列表是python中的可变数据类型。字符串、整数元组等都不是。那么可变性如何影响作用域呢?在这两个示例中作用域是完全相同的。在第一个示例中,使用增广赋值运算符+=
,对于不可变的数据结构,该运算符返回一个新对象并将其重新分配给局部变量。在该局部范围之外,这些效果不再可见。在第二个示例中,您使用了.append
,它在适当的位置对列表进行了修改,并且效果将在列表可访问的任何范围(即您的全局范围)中可见。此外,因为它需要重复:Python的求值策略对于可变或不可变类型也没有不同。它的工作原理完全相同。Python评估策略的技术名称是。这有时被称为“按对象值调用”,甚至只是“按值调用”,但它肯定不同于按引用调用,否则您将能够用Python编写一个真正的交换函数。这是错误的。Python的求值策略对于可变或不可变类型没有什么不同。它的工作原理完全相同。Python评估策略的技术名称是。这有时被称为“按对象值调用”,甚至只是“按值调用”,但它肯定不像按引用调用,否则您将能够用Python编写一个真正的交换函数。我只是想确定这不是范围问题,而是值如何更新的问题。很抱歉,这仍然是错误的。首先,您关于可变类型和不可变类型的评估策略差异的说法是完全错误的。第二,您没有更新“本地副本”,变量仍然包含对同一个不可变对象的引用。在Python中,永远不会使用赋值复制对象。不同之处在于,在一种情况下,您是在变异一个对象;在另一种情况下,当您使用增广赋值运算符(+=
)时,您是在重新分配一个局部变量以引用一个新对象。