【问题标题】:python recursive instance variables sharing datapython递归实例变量共享数据
【发布时间】:2013-12-23 07:14:40
【问题描述】:

我正在尝试创建一个创建树的递归函数。每个节点都持有井字游戏的状态,每个节点的子节点是下一个可能的移动。

我将棋盘的状态传递给递归函数。对于每一个可能的移动,我都会创建一个状态副本,然后进行移动。这个新状态被传递给递归函数。

#XXX
#O O = state [1,1,1,-1,0,-1,1,1,1]
#XXX

playerX = 1
playerO = -1

class node:
    children = [] #holds all the children
    state = [] #holds the state of the board as a list of ints
    def __init__(self,st):
        self.state = st

    def addChild(self,child):
        self.children.append(child) #if only giving birth was this easy

#returns a node with all it's children filled in
#cState is the state for this node
#currentPlayer flips sign every function call
#stateSize is the size of the board
def makeTreeXO(cState,currentPlayer,stateSize):
    newNode = node(cState)
    for i in range(stateSize):
        print "looking at", i, "in", cState
        if(cState[i] == 0): #found an open space
            print "found an empty space"
            newState = cState #create copy of state
            newState[i] = currentPlayer #make the available move
            print "made new state"
            newNode.addChild(makeTreeXO(newState,currentPlayer*-1,stateSize))
    print "Done with this instance"
    return newNode

root = makeTreeXO([1,0,0,1,1,1,1,1,1],playerX,9)

输出:

looking at 0 in [1, 0, 0, 1, 1, 1, 1, 1, 1]
looking at 1 in [1, 0, 0, 1, 1, 1, 1, 1, 1]
found an empty space
made new state
looking at 0 in [1, 1, 0, 1, 1, 1, 1, 1, 1]
looking at 1 in [1, 1, 0, 1, 1, 1, 1, 1, 1]
looking at 2 in [1, 1, 0, 1, 1, 1, 1, 1, 1]
found an empty space
made new state
looking at 0 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 1 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 2 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 3 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 4 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 5 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 6 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 7 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 8 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
Done with this instance
looking at 3 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 4 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 5 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 6 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 7 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 8 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
Done with this instance
looking at 2 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 3 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 4 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 5 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 6 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 7 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
looking at 8 in [1, 1, -1, 1, 1, 1, 1, 1, 1]
Done with this instance

从打印语句中可以清楚地看出,对状态所做的更改正在被带回函数的父实例。有谁知道为什么?

【问题讨论】:

    标签: python recursion tree tic-tac-toe


    【解决方案1】:

    问题是,您正在修改类变量,它们将被特定类的所有对象共享。要解决此问题,请像这样制作 statechildren 实例变量

    class node:
        def __init__(self,st):
            self.state = st
            self.children = []
    
        def addChild(self,child):
            self.children.append(child) #if only giving birth was this easy
    

    根据这一行,

    newState = cState #create copy of state
    

    您正在尝试创建cState 的副本并将其存储在newState 中。请记住,在 Python 中,赋值运算符永远不会将一个的值复制到另一个。它只是将左侧的变量指向赋值语句右侧表达式的评估结果。

    所以,你实际上在做的是,让newStatecState 指向同一个列表。所以,如果你修改newState,它也会影响cState。要实际创建列表的副本,您可以使用切片运算符,如下所示

    newState = cState[:] #create copy of state
    

    【讨论】:

      【解决方案2】:

      一个问题是这一行:

      newState = cState
      

      从您的评论看来,您正在尝试复制 cState,但是,您只是将 reference 复制到列表,而不是列表的内容。因此,当您修改 newState 时,您也在修改 cState。你应该拥有的是:

       newState = cState.copy()
      

      编辑 由于这是公认的答案,完整的解决方案还包括在节点的构造函数中设置self.children = [],如@thefourtheye 所述

      【讨论】:

      • 不是唯一的问题,而是一个重要的问题。这个问题和他们发现的类变量问题都是意外数据更改错误的来源。如果它出现,具有更复杂数据结构的读者应该记住copy() 是一个浅拷贝 - 对于某些结构,copy.deepcopy 是必需的。
      猜你喜欢
      • 2014-07-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-11
      • 2021-07-04
      • 2019-02-21
      • 1970-01-01
      相关资源
      最近更新 更多