2015-12-31 18 views
0

我的數據結構如下:嘗試從字典中的列表中刪除列表中的特定項目。 (Python2)

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.]]} 

我希望能夠創建爲單位的副本,但沒有鑰匙是一個字符串在自己的列表中。所以我想從前三個列表中刪除'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中的遊戲生成數獨遊戲。我打算使用units2中的列表來檢查並查看在任何給定列表中是否只有1個單元仍符合特定數字的條件。如果是這種情況,我知道該單元格必須具有該數字作爲它的值,因爲它是列,行或9x9塊中唯一可以根據規則合法保留它的值。

+0

什麼是跨職能? – Radek

回答

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

如果你看看這裏發生了什麼,並退一步了一會兒,答案是顯而易見的。

你肯定知道是什麼原因導致:

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,顯而易見的答案是你有多個對同一個列表的引用,並且當你從另一個列表中刪除時,結束清空所有你的結構中的列表。這裏有一個簡單的方法來檢查:其輸出(不出所料)

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])) 

Voilá!我們有我們的罪魁禍首!

在您的原始字典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 
}