不直观的UnboundLocalError行为的原因2

时间:2010-12-26 14:21:46

标签: python list global-variables scope

跟进Reason for unintuitive UnboundLocalError behaviour(我假设你已经阅读过了)。 请考虑以下Python脚本:

def f():
    # a+=1          # 1
    aa=a
    aa+=1

    # b+='b'        # 2
    bb=b
    bb+='b'

    c[0]+='c'       # 3
    c.append('c')
    cc=c
    cc.append('c')

    d['d']=5        # Update 1
    d['dd']=6       # Update 1
    dd=d            # Update 1
    dd['ddd']=7     # Update 1

    e.add('e')      # Update 2
    ee=e            # Update 2
    ee.add('e')     # Update 2

a=1
b='b'
c=['c']
d={'d':4}           # Update 1
e=set(['e'])        # Update 2
f()
print a
print b
print c
print d             # Update 1
print e             # Update 2

脚本的结果是:

1
b
['cc', 'c', 'c']
{'dd': 6, 'd': 5, 'ddd': 7}
set(['e'])

注释掉的行(标记为1,2)是通过UnboundLocalError的行,我引用的SO问题解释了原因。但是,标记为3的行有效!

默认情况下,列表在Python中通过引用复制,因此当cc更改时c更改是可以理解的。但是,如果Python不允许直接从方法的范围更改a和b,那么为什么Python允许c首先进行更改?

我不知道默认列表是如何通过Python中的引用复制的,这会使这个设计决策不一致。

我错过了什么人?

更新

  • 为了完整起见,我还添加了与上述问题等效的字典,即我添加了源代码,并使用# Update
  • 标记了更新
  • 为了进一步完整,我还添加了等效集。该集的行为对我来说实际上是令人惊讶的。我希望它的行为类似于列表和字典......

2 个答案:

答案 0 :(得分:3)

与字符串和整数不同,Python中的列表是可变对象。这意味着它们的设计可以改变。这条线

c[0] += 'c'

与说

相同
c.__setitem__(0, c.__getitem__(0) + 'c')

不会对名称c绑定的内容进行任何更改。在此次通话之前和之后,c是相同的列表 - 此列表的内容已更改。

你说过

c += ['c']
c = [42]
函数f()中的

,会出现相同的UnboundLocalError,因为第二行会使c成为本地名称,第一行会转换为

c = c + ['c']

要求名称c已经绑定到某个东西,(在本地范围内)它还没有。

答案 1 :(得分:2)

要考虑的重要一点是:(或b或c)指的是什么对象?第a += 1行正在改变a引用的整数。整数是不可变的,所以当从1变为2时,它与a = a + 1实际上是相同的,它提供了一个全新的整数来引用。

另一方面,c[0] += 'c'不会更改c引用的列表,它只会更改其第一个元素引用的字符串。列表是可变的,因此可以在不改变其标识的情况下修改相同的列表。