Python:对全局和局部变量感到困惑

时间:2016-10-31 16:49:48

标签: python

Noob问题。我正在编写一个带有递归函数调用的程序 - 这是一个游戏,所以我使用递归来让计算机通过尝试移动来“提前思考”。我将游戏状态保持在一个列表中,然后将其传递给一个改变游戏状态并递归调用自身9或10次的函数。当我尝试第一个版本时,似乎将列表视为全局变量。我做了一些测试,发现变量总是被视为本地变量,但是如果你改变函数内的列表(通常我做的事情就像board [i] =“X”那样简单),它会改变 global 列出而不是作用于函数内的本地列表。下面的小例子显示了我的意思:打印输出是[1,2],而如果我做相同的例子,但是使板成为整数而不是列表,则值在函数外部保持为1。

是否有一种简单的方法可以使python将列表视为函数内部的局部,记住每次递归调用函数时都需要这样做?

def test(board):
    board[1] = 2
    return 1

board = [1] * 2
print board

3 个答案:

答案 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,那么这就是我们所拥有的(按顺序):

  1. board:全局级别对象名称
  2. board:函数本地对象名称(作为函数定义的一部分)
  3. board:函数本地对象名称,但被重新分配给新对象
  4. board[:]:函数本地对象名称引用的对象的切片或副本
  5. 至关重要的是要认识到您只将 对象名称 传递给您的函数。您的困惑源于您传递对象本身的想法。但是,你不是。其中一个基本原因是python为你管理内存;如果您能够传递内存地址并删除其他语言中的实际对象,则很难跟踪存储在内存中的内容(例如,当不需要时可以删除内容) 。

    当全局对象名board传递给函数时,会创建一个新的函数本地对象名,但它仍然指向与全局对象名相同的对象。这个对象恰好是list,它是可变的。

    由于全局名称和函数本地名称都指向同一个可变对象,因此如果您更改该对象:

    board[1] = 2
    

    ...然后,通过本地名称或全局名称进行更改并不重要;它是同一个被更改的对象

    但是,当你在函数中执行此操作时:

    board = board[:]
    

    函数本地对象名称正在重新分配。它指向的对象没有变化!它只是使本地对象名称指向一个NEW对象而不是它之前指向的对象。在这种特殊情况下,它指向的新对象是旧对象的副本。但我们可以轻易地指出其他一些对象:

    board = "HELLO WORLD!" 
    

    顺便说一句,对于任何其他类型的可变(setlistdict)或不可变的int,{{1 }},floatstr)对象。唯一的区别是,由于无法更改不可变对象,因此它通常看起来好像是传递给该函数的该对象的副本。但事实并非如此;它只是一个函数本地名称,它指向与全局名称相同的对象...与可变对象相同。

答案 2 :(得分:0)

要点几点:

  • Python使所有未在函数范围内声明的变量成为全局变量。如果希望函数作用于全局变量,则可以重写函数以更改外部声明的变量;而不是直接使用变量本身并返回它,返回您希望在全局变量中更新的值。
  • 大多数语言中面向对象的方法都是“按引用传递”或“按值传递”类型。关于这个确切的主题有一个很好的讨论here

希望这有帮助!