我发现引用列表的变量的作用域与引用元组、整数或字符串的变量不同。为什么会这样?
1)当我使用整数,字符串或元组时:-
>>> 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的值更改仅限于函数范围,而在List范围更改的情况下。
'
这不是范围问题。作用域在类型之间没有区别。事实上,它不能有所不同:变量只是对值的命名引用,完全与类型无关。
但是 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
是不可变的类型,而list
是可变的类型。将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
也是可见的,并且可以在遍历函数中使用,如果没有对它的赋值,即 仅当存在第 12 行时,才会出现第 11 行UnboundLocalError
。由于 int 的不变性,第 12 行导致遍历函数中的变量prev
指向新的内存位置,然后"隐藏"外部定义的变量prev = 0
从而导致错误。
但是当一个新的 int 被分配给prev[0]
时,可变列表中的一个元素,列表第一个元素的指针可以更新为指向新的 int(遍历函数内部和外部); 没有创建单独的范围(即局部变量),原始prev
变量在遍历函数中仍然可见,prev[0]
可以在第 11 行使用,然后再在第 12 行分配。
不同之处在于不可变类型 int、string 和 tuple 是按值传递的。当函数更新值时,它正在更新本地副本。
列表通过引用传递。使用 append 时,它会更新原始列表。