2013-10-13 73 views
4
def set_if_not_there(d, fields, default_value=None): 
    for field in fields: 
     if not field in d: 
      d[field] = default_value 

d = { } 

set_if_not_there(d, ['cnt1', 'cnt2'], 0) 
set_if_not_there(d, ['tags1', 'tags2'], []) 

d['cnt1'] += 1 
d['tags1'].append('work') 

print d 

输出是:共享可变的问题?

{'tags2': ['work'], 'cnt2': 0, 'cnt1': 1, 'tags1': ['work']} 

正如你所看到的,tags1tags2实际上指的相同的列表,这不是预期的。 cnt1cnt2工作正常。

我该如何执行set_if_not_there,以便我创建可变的copies,但仅在需要时?也就是说,如果默认值是“标量”(int,字符串,None,...),则不需要副本,但对于列表和字符串,则需要副本。

回答

9

使用工厂函数而不是缺省值:

def set_if_not_there(d, fields, default_factory=None): 
    if default_factory is None: 
     default_factory = lambda: None 
    for field in fields: 
     if not field in d: 
      d[field] = default_factory() 

和可调用传递(如函数或lambda表达式,或默认类型):

set_if_not_there(d, ['cnt1', 'cnt2'], int) 
set_if_not_there(d, ['tags1', 'tags2'], list) 

int()返回0list()返回一个新的空单。例如,这也是标准库collections.defaultdict()类型所做的。

演示:

>>> d = {} 
>>> set_if_not_there(d, ['cnt1', 'cnt2'], int) 
>>> set_if_not_there(d, ['tags1', 'tags2'], list) 
>>> d['cnt1'] += 1 
>>> d['tags1'].append('work') 
>>> print d 
{'tags2': [], 'cnt2': 0, 'cnt1': 1, 'tags1': ['work']} 
+0

+1我需要打印这种做法在我的心里,因为它是如此充满了好点! –

+0

谢谢!任何违反'default_factory = default_factory或lambda:None'的情况? – dangonfast

+0

这不会让你检测到像'set_if_not_there(d,['cnt1','cnt2'],False'(你打算使用'lambda:False'来代替))的错误。 –

2

你只需要copy.deepcopy

import copy 
def set_if_not_there(d, fields, default_value=None): 
    for field in fields: 
     if not field in d: 
      d[field] = copy.deepcopy(default_value) 

d = { } 

set_if_not_there(d, ['cnt1', 'cnt2'], 0) 
set_if_not_there(d, ['tags1', 'tags2'], []) 

d['cnt1'] += 1 
d['tags1'].append('work') 

print d 

结果:

>>> 
{'tags2': [], 'cnt2': 0, 'cnt1': 1, 'tags1': ['work']} 

我认为我的方式更加灵活,因为你可以定义DEFAULT_VALUE不只是空one.if尝试:

set_if_not_there(d, ['cnt1', 'cnt2'], 0) 
set_if_not_there(d, ['tags1', 'tags2'], [0,1]) 

d['cnt1'] += 1 
d['tags1'].append('work') 

print d 

你会得到:

{'tags2': [0, 1], 'cnt2': 0, 'cnt1': 1, 'tags1': [0, 1, 'work']} 

如果你真的p是指以避免进口**,你可以尝试:

def set_if_not_there(d, fields, default_value=lambda:None): 
    for field in fields: 
     if not field in d: 
      d[field] = default_value() 

d = { } 

set_if_not_there(d, ['cnt1', 'cnt2'], lambda:0) 
set_if_not_there(d, ['tags1', 'tags2'], lambda:[0,1]) 

也适用