Python:为什么引用列表的变量的作用域与引用任何其他数据结构或数据类型的变量不同?



我发现引用列表的变量的作用域与引用元组、整数或字符串的变量不同。为什么会这样?

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 时,它会更新原始列表。

相关内容

  • 没有找到相关文章

最新更新