类内部的 Python 全局变量不通过类方法更新



我正在下国际象棋玩AI,并且有一个最小最大值算法,该算法当前在选择移动后返回游戏状态:棋盘,white_pieces和black_pieces。

我的国际象棋类处理移动生成和状态的维护。它有一些全局变量:

__board
__white_pieces
__black_pieces

在类中声明:body,但在任何方法之外。这应该使其成为全局变量。

在游戏开始时,这些是使用 build_board 方法初始化的(并不总是为了测试代码而实现(。我想现在我可以在这一点上移动到init(( 方法,因为我已经测试了代码。

def build_board(self):
for i in range(1, 17):
self.__board[self.__white_pieces[i][1]] = i
self.__white_pieces[i][3] = True
self.__board[self.__black_pieces[i][1]] = -1 * i
self.__black_pieces[i][3] = True
for i in range(0, 20):
self.__board[i] = 99
for i in range(100, 120):
self.__board[i] = 99
for i in range(0, 8):
self.__board[20 + 10 * i] = 99
self.__board[20 + 10 * i + 9] = 99

我假设这是有道理的,因为它使用了一个可变类型的列表。当然,没问题。这将编辑版块。

然后,当我尝试让游戏采用 minimax 返回的游戏状态并更新这些全局变量时,这些变量似乎都没有更新它:

def play_move(self,board,white_pieces,black_pieces):
global __board
global __white_pieces
global __black_pieces
__board = board
__white_pieces = white_pieces
__black_pieces = black_pieces

def play_move(self,board,white_pieces,black_pieces):
self.__board = board
self.__white_pieces = white_pieces
self.__black_pieces = black_pieces

我是否误解了全局变量在类中的工作方式?如果有人能为我澄清这一点,那就太好了。谢谢!

编辑: 我甚至可以让 playmove 方法返回 self.__board、self.__white_pieces、self.__black_pieces 元组并打印出来,如果它们实际上是更新的移动并且它们是。在该方法中,并将这些结果传递给另一个方法,表示self.__xxxxx已更新。但是,从该方法之外,它似乎不会更新全局变量。

声明"在类内:主体但在任何方法之外"的变量成为类属性,而不是全局变量。类属性在类的所有实例之间共享,因此这可能不是您想要的。

请注意,除非有同名的实例属性隐藏它,否则您可以从实例访问类属性(并改变它(,但是一旦您在实例上设置(而不是改变(此属性,它将创建一个将隐藏类属性的实例属性:

>>> class Foo(object):
...     bar = []
...     def add(self, x):
...        self.bar.append(x)
...     def set(self, bar):
...        self.bar = bar
... 
>>> f1 = Foo()
>>> f1.bar
[]
>>> id(f1.bar)
140281150134752
>>> f2 = Foo()
>>> f2.bar
[]
>>> id(f2.bar)
140281150134752
>>> f2.bar is f1.bar
True
>>> f1.add(42)
>>> f2.bar
[42]
>>> f1.bar
[42]
>>> f1.set([33])
>>> f1.bar
[33]
>>> f2.bar
[42]
>>> f2.bar is Foo.bar
True
>>> f1.bar is Foo.bar
False
>>> 

鉴于您的用例和代码片段,我认为您真的希望您的board等是实例属性而不是全局或类属性。

你提到:

每种方法都需要能够访问电路板的当前状态并对其进行修改。

但这正是拥有实例属性的意义所在:对象的所有方法都共享对象的状态(=> 属性(。

作为旁注,不要在你的属性前面加上双下划线,除非你真的知道它的作用并且真正需要它(提示:你可能每 10 年左右需要一次......单个前导下划线是表示它是实现细节而不是类 API 一部分的约定。

最新更新