在Python中,可以在不必返回任何内容的情况下更改函数中的某些类型的对象。对于某些对象,情况并非如此。
例如,如果我们将字典传递给函数并在函数中更改它,原始字典将被更改:
def add_key(d, key):
d[key] = True
d = {}
print d
# {}
add_key(d, 1)
print d
# {1: True}
相反,我们不能改变这样的整数:
def increment_int(i):
i += 1
i = 1
print i
# 1
increment_int(i)
print i
# still 1!
起初我认为规则可能只是不可变对象不能以这种方式在函数内改变。类似的示例不适用于字符串:
def append_string(s1, s2):
s1 += s2
s = "hello"
print s
# "hello"
append_string(s, " world")
print s
# "hello", not "hello world" :(
我很困惑,因为似乎应用于函数中的可变对象的某些操作将在之后可见,而其他操作则不会。例如。将项目附加到列表中确实有效:
def append_list(l, val):
l.append(val)
l = []
print l
# []
append_list(l, 1)
print l
# [1]
交换两个列表的名称时不会:
def swap_lists(l1, l2):
l1, l2 = l2, l1
l1 = [1]
l2 = [2]
print l1, l2
# [1] [2]
swap_lists(l1, l2)
print l1, l2
# still [1] [2], even though lists are mutable!
又一个例子,看似奇怪和意外的行为:
def swap_then_append(l1, l2):
l1, l2 = l2, l1
l1.append(1)
l2.append(2)
l1 = []
l2 = []
print l1, l2
# [] []
swap_then_append(l1, l2)
print l1, l2
# [2] [1]
有人能解释一下这里发生了什么吗?
编辑:此问题的possible duplicate已被确定;那里的答案部分地向我解释了发生了什么,我仍然不明白为什么,例如,上面的函数swap_lists
实际上没有交换列表。现在我正在进一步思考它,我仍然不明白为什么上面的increment_int
和append_string
没有达到我最初的预期,除了它与不变性有关整数和字符串。
答案 0 :(得分:3)
欺骗目标中的解释很好地解释了发生了什么,但我将解决swap_then_append
中发生的事情。
def swap_then_append(l1, l2):
print l1,l2
# [3],[4]
l1, l2 = l2, l1
print l1,l2
# [4] [3]
l1.append(1)
l2.append(2)
l1 = [3]
l2 = [4]
print l1, l2
# [3] [4]
swap_then_append(l1, l2)
print l1, l2
# [3,2] [4,1]
'交换'更改用于引用列表的变量的名称,但仅在函数中。虽然底层对象的变异是永久性的,但变量的分配取决于范围。函数中的新l1
和l2
也可能被称为其他内容,因为它们现在只是影响函数签名中定义的原始变量l1
和l2
,反过来,它会影响主脚本中定义的l1
和l2
。然后突变适用于原始列表。