尝试这三个问题,然后在文章末尾检查答案。
提示:任务有一些共同点,因此请重新思考第一个任务的解决方案,当您继续执行第二个或第三个任务时,这对您来说会更容易。
第一项任务
有几个变量:
x = 1
y = 2
l = [x, y]
x += 5
a = [1]
b = [2]
s = [a, b]
a.append(5)
打印时显示什么
l
和s
?
第二项任务
让我们定义一个简单的函数:
def f(x, s=set()):
s.add(x)
print(s)
如果您拨打电话会发生什么:
>>f(7)
>>f(6, {4, 5})
>>f(2)
第三项任务
我们定义了两个简单的函数:
def f():
l = [1]
def inner(x):
l.append(x)
return l
return inner
def g():
y = 1
def inner(x):
y += x
return y
return inner
执行这些命令后会得到什么?
>>f_inner = f()
>>print(f_inner(2))
>>g_inner = g()
>>print(g_inner(2))
您对答案的信心如何?让我们检查一下您的情况。
第一个问题的解决方案
>>print(l)
[1, 2]
>>print(s)
[[1, 5], [2]]
为什么第二个列表响应第一个元素的更改
a.append(5)
,而第一个列表完全忽略相同的更改x+=5
?
解决第二个问题
让我们看看发生了什么:
>>f(7)
{7}
>>f(6, {4, 5})
{4, 5, 6}
>>f(2)
{2, 7}
等等,最后的结果不是
{2}
吗?
解决第三个问题
结果将是这样的:
>>f_inner = f()
>>print(f_inner(2))
[1, 2]
>>g_inner = g()
>>print(g_inner(2))
UnboundLocalError: local variable ‘y’ referenced before assignment
g_inner(2)
她
为什么不出卖3
?为什么内部函数会f()
记住外部作用域,而内部函数g()
却不记得?他们几乎一样!
说明
如果我告诉您所有这些奇怪的行为都与Python中可变对象和不可变对象之间的差异有关怎么办?
可以在本地修改列表,集合或字典等可修改对象。不可修改的对象,例如数字和字符串值,元组,无法修改;它们的“改变”将导致新对象的创建。
第一项任务的说明
x = 1
y = 2
l = [x, y]
x += 5
a = [1]
b = [2]
s = [a, b]
a.append(5)
>>print(l)
[1, 2]
>>print(s)
[[1, 5], [2]]
由于它是
x
不可变的,因此该操作x+=5
不会更改原始对象,而是会创建一个新对象。但是列表中的第一项仍然引用原始对象,因此其值不会改变。
因为 一个可变的对象,然后该命令
a.append(5)
修改原始对象(而不是创建一个新的对象),并且列表s
“看到”更改。
第二项任务的说明
def f(x, s=set()):
s.add(x)
print(s)
>>f(7)
{7}
>>f(6, {4, 5})
{4, 5, 6}
>>f(2)
{2, 7}
通过前两个结果,一切都很清楚:将第一个值
7
添加到最初为空的集合中,结果为{7}
;然后将该值6
添加到集合中{4, 5}
,结果是{4, 5, 6}
。
然后奇怪的事情开始了。该值不
2
添加到空集,而是添加到{7}。为什么?可选参数的初始值s
仅计算一次:在第一次调用时,s将被初始化为空集。并且由于它是可变的,f(7)
因此在被调用后将在适当的位置进行更改。第二个调用f(6, {4, 5})
不会影响默认参数:集合将替换它{4, 5}
,即它是{4, 5}
一个不同的变量。第三次调用f(2)
使用相同的变量s
第一次调用时使用,但并未将其重新初始化为空集,而是从其先前的值中获取{7}
。
因此,不应将可变参数用作默认参数。在这种情况下,需要更改功能:
def f(x, s=None):
if s is None:
s = set()
s.add(x)
print(s)
第三项任务的说明
def f():
l = [1]
def inner(x):
l.append(x)
return l
return inner
def g():
y = 1
def inner(x):
y += x
return y
return inner
>>f_inner = f()
>>print(f_inner(2))
[1, 2]
>>g_inner = g()
>>print(g_inner(2))
UnboundLocalError: local variable ‘y’ referenced before assignment
在这里,我们处理闭包:内部函数记住定义它们时外部命名空间的外观。或至少他们应该记住,但是第二个功能使扑克牌看起来更像是没有听说过其外部名称空间。
为什么会这样呢?当我们执行
l.append(x)
时,定义函数时创建的可变对象会更改。但是该变量l
仍然引用旧的内存地址。但是,尝试在第二个函数中更改不可变变量会y += x
导致y开始引用不同的内存地址:原始y将被遗忘,这将导致UnboundLocalError。
结论
Python中可变对象与不可变对象之间的区别非常重要。避免本文中描述的怪异行为。特别:
- .
- - .