Noob问题。我正在编写一个带有递归函数调用的程序 - 这是一个游戏,所以我使用递归来让计算机通过尝试移动来“提前思考”。我将游戏状态保持在一个列表中,然后将其传递给一个改变游戏状态并递归调用自身9或10次的函数。当我尝试第一个版本时,似乎将列表视为全局变量。我做了一些测试,发现变量总是被视为本地变量,但是如果你改变函数内的列表(通常我做的事情就像board [i] =“X”那样简单),它会改变 global 列出而不是作用于函数内的本地列表。下面的小例子显示了我的意思:打印输出是[1,2],而如果我做相同的例子,但是使板成为整数而不是列表,则值在函数外部保持为1。
是否有一种简单的方法可以使python将列表视为函数内部的局部,记住每次递归调用函数时都需要这样做?
def test(board):
board[1] = 2
return 1
board = [1] * 2
print board
答案 0 :(得分:2)
在Python中,有两种类型的对象, mutable 和 immutable 。
Mutable 对象包括任何用户创建的对象,以及一些包含list
的内置对象,而不可变对象就像{{1} },String
和其他原始类型。
如果将 mutable 对象传入方法,该方法会获取引用到同一个对象,因此您可以根据需要对其进行操作和变异,被称为传递参考。如果传入 immutable 对象,则该对象将通过赋值传递,该赋值将为新的 local 变量赋值,其值与传递的参数相同。
在您的情况下,您将int
对象传递给您的方法,如上所述,它通过引用传递,因此您在方法中获得的是参考调用方法的范围内的对象。为了能够创建一个可以更改和变异而不影响外部列表的本地列表,您有两种选择:
将传递的参数分配给新变量,并从那里更改并改变该变量。
使用完整的非突变分配(例如分配整个列表而不仅仅是索引处的值),这将取消您的变量与参考的链接。
例如:
list
将输出
def mutate (my_list):
my_list.append(0)
my_list = [1, 2]
print(my_list)
mutate(my_list)
print(my_list)
而
[1, 2]
[1, 2, 0]
将输出
def mutate (my_list):
my_list = my_list + [0]
my_list = [1, 2]
print(my_list)
mutate(my_list)
print(my_list)
答案 1 :(得分:1)
是:复制清单。
board = board[:]
您描述该行为的方式表明您对python如何处理对象名称和名称空间有误解。我试着解释一下。
说我们这样做:
# test.py
board = [1,2,3]
def test(board):
board = board[:]
名称" board"出现4次以上。如果我们直接从命令行运行test.py,那么这就是我们所拥有的(按顺序):
board
:全局级别对象名称board
:函数本地对象名称(作为函数定义的一部分)board
:函数本地对象名称,但被重新分配给新对象board[:]
:函数本地对象名称引用的对象的切片或副本至关重要的是要认识到您只将 对象名称 传递给您的函数。您的困惑源于您传递对象本身的想法。但是,你不是。其中一个基本原因是python为你管理内存;如果您能够传递内存地址并删除其他语言中的实际对象,则很难跟踪存储在内存中的内容(例如,当不需要时可以删除内容) 。
当全局对象名board
传递给函数时,会创建一个新的函数本地对象名,但它仍然指向与全局对象名相同的对象。这个对象恰好是list
,它是可变的。
由于全局名称和函数本地名称都指向同一个可变对象,因此如果您更改该对象:
board[1] = 2
...然后,通过本地名称或全局名称进行更改并不重要;它是同一个被更改的对象。
但是,当你在函数中执行此操作时:
board = board[:]
函数本地对象名称正在重新分配。它指向的对象没有变化!它只是使本地对象名称指向一个NEW对象而不是它之前指向的对象。在这种特殊情况下,它指向的新对象是旧对象的副本。但我们可以轻易地指出其他一些对象:
board = "HELLO WORLD!"
顺便说一句,对于任何其他类型的可变(set
,list
,dict
)或不可变的int
,{{1 }},float
,str
)对象。唯一的区别是,由于无法更改不可变对象,因此它通常看起来好像是传递给该函数的该对象的副本。但事实并非如此;它只是一个函数本地名称,它指向与全局名称相同的对象...与可变对象相同。
答案 2 :(得分:0)
要点几点:
希望这有帮助!