python 中树节点的大量变化字典

large number of changing dictionaries for tree nodes in python

我正在尝试实现一个分层树结构,其中每个节点都有一个略有变化的字典版本。我的问题是,与 R 中的类似结构不同,python 字典只是外部变量的标签,而不是真正的 'value containers'。因此,在其中一个节点所做的任何更改也会影响所有其他节点的字典。

鉴于 dict 的这种行为,在 python 中实现它的正确方法是什么?这似乎是一种常见的方法,所以我觉得我一定遗漏了一些东西,但我已经用头撞墙好几个小时了。

背景: 我正在尝试在 python 中使用棋盘状态字典为基于回合的完美信息对抗性棋盘游戏实施 Minimax 方法。我根据所有可能的移动创建了节点和子级的层次树结构,到目前为止,我一直在尝试修改每个节点的字典。我不清楚 python 中字典的真实性质,所以我一直在与我的方法的奇怪结果作斗争,因为每个节点的更改也应用于所有其他节点。

例子

#Create some original state (dict of dicts)
state_original = {'field1' : {'player':1, 'count':2}, 'field2' : {'player':2, 'state': 4}}
print(state_original)

#Define object for the tree nodes
class Node(object):
  def __init__(self, depth, state, field=None):
    self.depth = depth
    self.state = state
    self.field = field
    self.subnodes = []
    if self.depth > 0:
      self.SpawnSubnodes()
  def SpawnSubnodes(self):
    for field in self.state:
      depth_new = self.depth -1
      state_new = self.state
      state_new[field]['count'] += 1
      self.subnodes.append(Node(depth_new, state_new, field))

#Build tree
nodes = Node(3, state_original)
nodes.subnodes

#But: This is a mess now
print(state_original)

#This is a mess, too. Results are not meaningful :( 
print(nodes.subnodes[1].state)

它适用于 deepcopy,但对于我的(较大的)树来说太慢了

from copy import deepcopy

#Define object for the tree nodes
class Node(object):
  def __init__(self, depth, state, field=None):
    self.depth = depth
    self.state = state
    self.field = field
    self.subnodes = []
    if self.depth > 0:
      self.SpawnSubnodes()
  def SpawnSubnodes(self):
    for field in self.state:
      depth_new = self.depth -1
      state_new = deepcopy(self.state)
      state_new[field]['count'] += 1
      self.subnodes.append(Node(depth_new, state_new, field))

编辑: 我意识到复制对我不起作用,因为我的董事会状态是字典的字典而不是简单的字典。我更新了我的示例代码以准确反映这一点。 虽然一个潜在的解决方法是尝试提出一个更简单的董事会表示(可能将其分成 "board shape" 字典和 "board state" 字典),但我觉得应该有更多 pythonic如何解决这个问题?

而不是 copy.deepcopy,请使用 copy.copy(浅拷贝),因为您并不真正需要 deep copy

import copy

#Define object for the tree nodes
class Node(object):
  def __init__(self, depth, state, field=None):
    self.depth = depth
    self.state = state
    self.field = field
    self.subnodes = []
    if self.depth > 0:
      self.SpawnSubnodes()
  def SpawnSubnodes(self):
    for field in self.state:
      depth_new = self.depth -1
      state_new = copy.copy(self.state)
      state_new[field] += 1
      self.subnodes.append(Node(depth_new, state_new, field))

浅拷贝比深拷贝快得多。这是一个简单的计时测试:

In [5]: %timeit copy.deepcopy(state_original)
The slowest run took 6.96 times longer than the fastest. This could mean that an intermediate result is being cached 
100000 loops, best of 3: 4.97 µs per loop

In [6]: %timeit copy.copy(state_original)
The slowest run took 8.84 times longer than the fastest. This could mean that an intermediate result is being cached 
1000000 loops, best of 3: 709 ns per loop

注意:上述解决方案仅在有问题的字典是简单时有效,即它不包含其他字典。

如果开头的字典包含其他字典 simple-dicts,迭代地执行其内容的浅拷贝可能比深拷贝操作更快。

def mycopy(d):
    return {k: copy.copy(v) for k, v in d.items()}

mycopy 的初步性能分析使我大致比 copy.deepcopy 有了 order-of-magnitude 的改进。