尝试从字典中的列表中的列表中删除特定项目。 (Python2)

时间:2015-12-31 14:07:25

标签: python list dictionary

我的数据结构如下:

def cross(A, B):
    return [a+b for a in A for b in B]

digits = '123456789'
rows = 'ABCDEFGHI'
cols = digits
squares = cross(rows, cols)
unitlist = ([cross(rows, c) for c in cols] +
            [cross(r, cols) for r in rows] +
            [cross(rs, cs) for rs in ('ABC', 'DEF', 'GHI') for cs in ('123', '456', '789')])
units = dict((s, [u for u in unitlist if s in u]) for s in squares)
peers = dict((s, set(sum(units[s],[]))-set([s])) for s in squares)

这会导致单位输出

{'A1': [['A1','A2','A3','A4','A5','A6','A7','A8','A9'], 
        ['A1','B1','C1','D1','E1','F1','G1','H1','I1'], 
        ['A1','A2','A3','B1','B2','B3','C1','C2','C3']], 
 'A2': [['A1','A2','A3','A4','A5','A6','A7','A8','A9'],       
        ['A2','B2','C2','D2','E2','F2','G2','H2','I2'], 
        ['A1','A2','A3','B1','B2','B3','C1','C2','C3']],
 'A3': [[etc.]]} 

我希望能够创建单元的副本,但是不要让KEY成为其自己的列表中的字符串。所以我希望从前3个列表中删除'A1'。 'A2'从接​​下来的3个名单中删除等等。

我可以导入副本并执行units2 = copy.deepcopy(units)然后units2['A1'][0].remove('A1')一次执行一个列表。所以这让我尝试制作一个循环来同时完成所有操作。

for s in squares: #assign s to 'A1' then 'A2', etc.
    for x in range(3): #
        units2[s][x].remove(s)

我认为通过运行

可以完美地完成它
units2['A1'][0].remove('A1')
units2['A1'][1].remove('A1')
units2['A1'][2].remove('A1')
units2['A2'][0].remove('A2')
# etc.

不幸的是,在运行此循环后,我最终得到:

units2 = {'B8': [[], [], []], 
          'H1': [[], [], []],
          'C7': [[], [], []],
          'B3': [[], [], []],
          # etc.
          }

所以不知何故,这个循环正在删除列表中的所有数据,而不仅仅是当前[x]列表中的当前[s]。

我尝试过以不同的方式构建列表,甚至制作了81个版本的

for x in range(3):
    units2['A1'][x].remove('A1')
for x in range(3):
    units2['A2'][x].remove('A2')
for x in range(3):
    units2['A3'][x].remove('A3')

但我仍然以空列表作为我的所有词典值。

*我正在尝试做的是构建一个数独求解器然后用于在PyGame中为游戏生成sudokus。我计划使用units2中的列表来检查并查看任何给定列表中是否只有1个单元格仍然符合特定号码的条件。如果是这种情况,我知道该单元格必须将该数字作为其值,因为它是列,行或9x9块中唯一可以根据规则合法保留它的块。

2 个答案:

答案 0 :(得分:1)

使用嵌套列表推导更改单位:

units = dict((s, [[e for e in u if s != e] 
                        for u in unitlist if s in u]) for s in squares)

答案 1 :(得分:1)

如果你看一下这里发生了什么,然后退一步,答案就变得很明显了。

毫无疑问,你知道是什么导致这种情况:

In [1]: a = [1,2,3]
In [2]: b = a   
In [3]: a.remove(2)
In [4]: print(a)
[1, 3]

In [5]: print(b)
[1, 3]

这就是你copy.deepcopy的原因。但是当你看看发生了什么时,如果你使用Occam's Razor,显而易见的答案就是你有多个对同一个列表的引用,当你从另一个列表中删除最终清空 all 您的结构中的列表。这是一个简单的方法来检查:

import collections
import pprint

counter = collections.Counter()
for s in squares:
    for x in range(3):
        counter[id(units2[s][x])] += 1

pprint.pprint(counter)

哪些输出(不出所料):

Counter({140502086261512: 9,
         140502086288328: 9,
         140502086261256: 9,
         140502086287688: 9,
         140502086288008: 9,
         # and so on...
         })

显然,您的id会有所不同,但计数都是9

你也可以在循环时使用pdbpudb检查变量。

如果您将units2替换为units,您将获得相同的计数。所以这里真正的问题是为什么copy.deepcopy没有像你想象的那样表现?为什么列表列表中有相同列表的多个副本?

让我们尝试一些实验:

list_of_lists = [[1,1,1],
                 [2,2,2],
                 [3,3,3],
                 ]
deepcopy_of_lists = copy.deepcopy(list_of_lists)
print(id(list_of_lists))
print(id(deepcopy_of_lists))

我希望这里有两个不同的id,实际上它们是。但他们的内容呢?

print(id(list_of_lists[0]))
print(id(deepcopy_of_lists[0]))

是的。那些是不同的。如果我们把它们放在字典中怎么办?

dict_of_list_of_lists = {'list_of_lists': list_of_lists}
deepcopy_of_dict = copy.deepcopy(dict_of_list_of_lists)
print(id(dict_of_list_of_lists['list_of_lists'][0]))
print(id(deepcopy_of_dict['list_of_lists'][0]))

我们仍然获得不同的id。一切似乎都像预期的那样表现,所以显然我们错过了一些东西。哦,是的 - 我们在复制的列表和原始列表之间进行比较。

  

如果您将units2替换为units,则会获得相同的计数。

有一个提示。另外,如果您阅读deepcopy documentation

  

深拷贝操作经常存在两个问题:浅拷贝操作不存在:

     
      
  • 递归对象(直接或间接包含对自身的引用的复合对象)可能会导致递归循环。
  •   
  • 因为深拷贝会复制它可能复制的所有内容,例如,即使在副本之间也应该共享的管理数据结构。
  •   
     

deepcopy()函数通过以下方式避免了这些问题:

     
      
  • 保留当前复制过程中已复制的对象的“备忘录”字典;和
  •   
  • 让用户定义的类覆盖复制操作或复制的组件集。
  •   

deepcopy会关注已经看过的对象

mylist = [1,2,3]
list_of_mylist = [mylist, mylist, mylist]
deepcopy_of_mylist = copy.deepcopy(list_of_mylist)

print(id(deepcopy_of_mylist[0]))
print(id(deepcopy_of_mylist[1]))

瞧!我们有罪魁祸首!

在原始字典units中,您有多个副本到不同键中的相同列表。这是因为这一行:

units = dict((s, [u for u in unitlist if s in u]) for s in squares)

本节特别说明:

[u for u in unitlist if s in u]

您在整个字典中重复对unitlist列表的引用。幸运的是,这是一个微不足道的修复:

[u[:] for u in unitlist if s in u]

每次都会制作该列表的副本,其余代码也能正常工作。

import pprint
import copy
import pprint

def cross(A, B):
        return [a+b for a in A for b in B]

digits = '123456789'
rows = 'ABCDEFGHI'
cols = digits
squares = cross(rows, cols)
unitlist = ([cross(rows, c) for c in cols] +
                        [cross(r, cols) for r in rows] +
                        [cross(rs, cs) for rs in ('ABC', 'DEF', 'GHI') for cs in ('123', '456', '789')])

units = dict((s, [u[:] for u in unitlist if s in u]) for s in squares)
peers = dict((s, set(sum(units[s],[]))-set([s])) for s in squares)

units2 = copy.deepcopy(units)
for s in squares: #assign s to 'A1' then 'A2', etc.
    for x in range(3): #
        units2[s][x].remove(s)

pprint.pprint(units2)

输出:

{'A1': [['B1', 'C1', 'D1', 'E1', 'F1', 'G1', 'H1', 'I1'],
        ['A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'],
        ['A2', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']],
 'A2': [['B2', 'C2', 'D2', 'E2', 'F2', 'G2', 'H2', 'I2'],
        ['A1', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'],
        ['A1', 'A3', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']],
 'A3': [['B3', 'C3', 'D3', 'E3', 'F3', 'G3', 'H3', 'I3'],
        ['A1', 'A2', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'],
        ['A1', 'A2', 'B1', 'B2', 'B3', 'C1', 'C2', 'C3']],
 # and so on and so forth
 }