所以我正在 python 3.3 中使用列表,这是我的示例代码:
def change_to_z(lis):
lis[3] = 'z'
def change_to_k(lis):
lis[4] = 'k'
def split(lis):
lis = lis[3:] + lis[:3]
totest = ['a', 'b', 'c', 'd', 'e', 'f']
change_to_z(totest)
print(totest)
change_to_k(totest)
print(totest)
split(totest)
print(totest)
和输出:
['a', 'b', 'c', 'z', 'e', 'f']
['a', 'b', 'c', 'z', 'k', 'f']
['a', 'b', 'c', 'z', 'k', 'f']
请注意,当我调用前两个函数时,我能够修改列表,而 totest 始终引用该列表,即使它已更改。
但是,使用第三个函数时,变量 totest 不再引用列表的最新修改版本。我的调试器告诉我,在函数“split”内,列表被翻转,但在函数之外,它没有翻转。为什么变量名不再引用列表了?
为什么会出现这种情况?哪些运营商会发生这种情况?为什么有时变量名在函数中修改后仍然引用列表,但与其他运算符的行为不同?
你对函数的
scope
的理解是错误的。
如果您确实想更改提供的列表,您可以尝试以下两种方法之一。
def split(lis):
global lis
lis = lis[3:] + lis[:3]
或者更好
def split(lis):
lis[:] = lis[3:] + lis[:3] # Credits go to drewk for pointing this out
当你做的时候
def split(lis):
lis = lis[3:] + lis[:3]
在
split
函数中,您正在创建一个 本地列表,其标识符与所提供的列表的标识符相同。但是,这两者不会冲突,因为传递的列表是global。
如果您想尝试一下并了解它们是否真的不同,请使用
id()
lis = [1, 2, 3]
def split(lis):
lis = lis[3:] + lis[:3]
print id(lis)
split(lis)
print id(lis)
上述代码的输出
40785920
40785600
注意内存位置有何不同
对
lis
的赋值是局部变量,不会更新列表。这在程序的python辅导可视化中很容易看到。
此外,请参阅这篇博文,了解正在发生的事情的清晰解释。这是一个简短的回顾:
在您的代码中,使用 global 声明来查看它与默认本地分配的工作方式有何不同:
def split(lis):
global lis
lis = lis[3:] + lis[:3]
您可以使用模块memory_graph:
https://pypi.org/project/memory-graph/
绘制数据图表并轻松查看变量之间共享哪些数据,以便更好地理解为什么
totest
没有更改。仅打印数据不会显示共享的数据。
import memory_graph
def change_to_z(lis):
lis[3] = 'z'
def change_to_k(lis):
lis[4] = 'k'
def split(lis):
memory_graph.show(memory_graph.get_call_stack(), block=True) # <--- graph1
lis = lis[3:] + lis[:3] # <--- assigns 'lis' a new list
memory_graph.show(memory_graph.get_call_stack()) # <--- graph2
totest = ['a', 'b', 'c', 'd', 'e', 'f']
change_to_z(totest)
print(totest)
change_to_k(totest)
print(totest)
split(totest)
print(totest)
调用
split(lis)
时,首先 lis
函数中的 split()
变量和 totest
变量共享数据,如图 1 所示:
但是随后您为
lis
分配了一个新值,这使得 lis
变量引用新列表,但这不会更改 totest
变量,如图 2 所示:
没有简单的方法可以在 Python 中使用 call-by-reference 以便
split()
将新列表也分配给 totest
,因此最好寻找不同的方法。但如果您确实需要,您可以将 totest
包装在另一个列表中,并使用 [0]
索引在任何地方访问它。现在代码变得很难阅读,所以不建议这样做!
import memory_graph
def change_to_z(lis):
lis[0][3] = 'z'
def change_to_k(lis):
lis[0][4] = 'k'
def split(lis):
memory_graph.show(memory_graph.get_call_stack(), block=True)
lis[0] = lis[0][3:] + lis[0][:3] # <--- assigns 'lis[0]' a new list
memory_graph.show(memory_graph.get_call_stack())
totest = [ ['a', 'b', 'c', 'd', 'e', 'f'] ] # <--- wrap in another list
change_to_z(totest)
print(totest[0])
change_to_k(totest)
print(totest[0])
split(totest)
print(totest[0])
如果我们绘制该程序的执行情况,我们首先会看到:
分配后我们看到:
并且
totest
变量现在已根据需要更新。
完全披露:我是memory_graph的开发者。