>>> def a():
... print "a executed"
... return []
...
>>>
>>> def b(x=a()):
... x.append(5)
... print x
...
a executed
>>> b()
[5]
>>> b()
[5, 5]
首次定义函数b时,x绑定到空列表对象。每次调用b时都会修改空列表对象,因为b绑定到对象。
我不知道的是当不可变对象发生这种情况时:
>>> def a():
... print "a executed"
... return 0
...
>>>
>>> def b(x=a()):
... x = x + 2
... print x
...
a executed
>>> b()
2
>>> b()
2
从我的POV开始,当首次定义函数b时,x绑定到int对象0。然后,在调用b()时修改x。因此,对b()的后续调用应该将x重新绑定到2,4,6等等。为什么不发生这种情况?我显然在这里缺少一些重要的东西!
Thx:)
答案 0 :(得分:4)
当您执行x =
时,您并未修改x
引用的对象,您只是将引用x
更改为指向其他对象,这种情况下,另一个int
。在这种情况下,无论x
是否指向不可变对象,它的事件都无关紧要。如果您使用列表进行x = x + [5]
,它也将保持不变。请注意区别:
def b1(x = []):
x = x + [5]
print(x)
def b2(x = []):
x.append(5)
print(x)
print("b1:")
b1()
print("b1:")
b1()
print("b2:")
b2()
print("b2:")
b2()
给出:
b1:
[5]
b1:
[5]
b2:
[5]
b2:
[5, 5]
当执行该功能时,您正在处理使用默认值初始化或由调用者提供的本地变量x
。因此,反弹的是局部变量x
,而不是参数的默认值。
您可能还想了解正式和实际参数之间的区别。它与此问题只有轻微关系,但可以帮助您更好地理解这一点。可以找到示例解释here。
答案 1 :(得分:2)
小心,两者之间存在巨大差异:
x.append(5)
和
x = x + 1
即,第一个改变x
引用的对象,而第二个创建一个新对象,该对象是x + 1
的结果,并将其重新绑定到名称x
。
当然,这有点过分简化 - 例如如果您使用过+=
怎么办...
它实际上取决于__add__
和__iadd__
的定义方式,但这应该得到重点...
为了更深入,您可以将函数视为对象或类的实例。它有一些特殊的属性,你甚至可以查看:
>>> def foo(x = lst): pass
...
>>> foo.func_defaults
([],)
>>> foo.func_defaults[0] is lst
True
定义函数后,func_defaults
1 将被设置。每次调用该函数时,python都会查看默认值和调用中存在的内容,并确定哪些默认值传递给函数以及哪些函数已经提供。需要注意的是,这就是为什么当你在第一种情况下附加到列表时,更改仍然存在 - 你实际上也在改变func_defaults
中的值。在您使用x = x + 1
的第二种情况下,您实际上并没有做任何改变func_defaults
的事情 - 您只是在创建新的东西并将其放入函数的命名空间。
1 该属性在python3.x中仅为__defaults__