2017-10-10 128 views
1

我有一個程序,其中必須在模擬中每年複製一些屬性的字典,並且必須更新一些鍵。但是,當我將一個字典發送給一個函數時,在函數內部修改它並返回它,返回的字典保留爲原始字典的引用。讓我用下面的代碼展示一個簡單的例子。返回輸入字典保留參考

def change(dict_in): 
    dict_in['value'] = 50 
    return dict_in 

props = [{'value':12}] 
props.append(change(props[-1])) 
props 
[{'value': 50}, {'value': 50}] 

但是,正如人們可以在上面看到的那樣,屬性'值'在第一個字典中也被改變了。

當我使用copy.deepcopy功能比它按預期工作:

import copy 
props = [{'value':12}] 
props.append(change(copy.deepcopy(props[-1]))) 
props 
[{'value': 12}, {'value': 50}] 

但是,這使它工作的唯一途徑!?

+0

你可以使用字典 – amrit

+0

'更新的更新方法()'將取代'變化()'函數是有用的,但改變了原來的字典了。 – zezollo

回答

1

dict.update()方法

def change(dict_in): 
    new_dict = {} 
    new_dict.update(dict_in) 
    new_dict['value'] = 50 
    return new_dict 

props = [{'value':12}] 
props.append(change(props[-1])) 
props 
[{'value': 50}, {'value': 50}] 

d1.update(d2)不返回的結果,它爲包括d2值修改d1。這就是爲什麼我首先創建一個新的空dict並將輸入值複製到它。

dict()方法

def change(dict_in): 
    new_dict = dict(dict_in) 
    new_dict['value'] = 50 
    return new_dict 

props = [{'value':12}] 
props.append(change(props[-1])) 
props 
[{'value': 50}, {'value': 50}] 

到以前的版本相似,但使用dict構造

dict理解方法

def change(dict_in): 
    new_dict = {k: v for k, v in dict_in.items()} 
    new_dict['value'] = 50 
    return new_dict 

props = [{'value':12}] 
props.append(change(props[-1])) 
props 
[{'value': 50}, {'value': 50}] 

而且使用dict推導另一個副本方法。

**kwargs方法

def change(**kwargs): 
    kwargs['value'] = 50 
    return kwargs 

props = [{'value':12}] 
props.append(change(**props[-1])) 
props 
[{'value': 50}, {'value': 50}] 

(在def線)的函數的形式參數之前**符號意味着未明確地指定的關鍵字參數將被存儲爲一個dictkwargs是這個變量的通用名稱。單個*對於將它們存儲在list中的位置參數的工作方式類似。

調用函數時的**表示法意味着相反,將dict值抽取到關鍵字參數中。與*和列表相同。

這樣我們將props[-1]這是原始dict提取到一組關鍵字參數並創建一個新的dict**kwargs。當你讓Python處理新的dict創建時,我實際上很喜歡這種方法,但在調用更改時必須記住使用**

0

如果您在deepcopyupdate上看到的問題是您的模擬每次迭代後都會創建一個字典副本,那麼可以考慮使用不可變的字典類型。不幸的是,python標準庫不提供不可變的字典。但是,數據結構可在庫中使用,如pyrsistent。從pmap的pyristent文檔:

>>> from pyrsistent import m, pmap, v 

# No mutation of maps once created, instead they are 
# "evolved" leaving the original untouched 
>>> m1 = m(a=1, b=2) 
>>> m2 = m1.set('c', 3) 
>>> m3 = m2.set('a', 5) 
>>> m1 
pmap({'a': 1, 'b': 2}) 
>>> m2 
pmap({'a': 1, 'c': 3, 'b': 2}) 
>>> m3 
pmap({'a': 5, 'c': 3, 'b': 2}) 
>>> m3['a'] 
5 
+0

這種方法每次迭代都會創建一個新的類似dict的對象。你只需要離開一個外部庫就可以了。如果有更簡單的解決方案,我不會僅爲此功能提供外部庫。 – Adirio

+0

@Adirio不,不可變數據結構不能這樣工作。 '.set()'的結果不會創建字典中所有值的副本。 'm2'只是在原始詞典'm1'上改變了'c'鍵的不同視圖。這是'm2'唯一的跟蹤。 –

+0

來自[Pyrsistent Github](https://github.com/tobgu/pyrsistent):「_數據結構上的所有方法通常會對它進行變異,而不是返回包含請求更新的結構的新副本。 untouched._「 – Adirio

0

如果它適合你的程序,你可以把數據分爲兩個部分:恆基地保持不變(和你現在正在從一個模擬一輪的複製下一個)和其他,即更改或更新。您可以使用ChainMap加入這兩部分。它在Python 3中可用,但如果您使用Python 2,它可能會支持它。

以下是一個示例。

from collections import ChainMap 

base = dict(a=1, b=2, c=3, d=4) 

updates = [ 
    dict(a=99), 
    dict(b=99), 
    dict(a=0, b=0, c=0), 
    ] 

for i, update in enumerate(updates, 1): 
    combined = ChainMap(update, base) 
    print("#{}: a={}, b={} c={} d={}".format(
     i, combined['a'], combined['b'], combined['c'], combined['d'])) 

#1: a=99, b=2 c=3 d=4 
#2: a=1, b=99 c=3 d=4 
#3: a=0, b=0 c=0 d=4