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']}
如您所见,tags1
和tags2
实际上是指同一个列表,这不是预期的。 cnt1
和cnt2
工作正常。
如何实现set_if_not_there
以便创建可变的copies
,但仅在需要时?也就是说,如果默认值是“标量”(int,string,None
,...),则不需要复制,但对于列表和dicts,则需要复制。
答案 0 :(得分: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()
并传入callables(如函数或lambdas或默认类型):
set_if_not_there(d, ['cnt1', 'cnt2'], int)
set_if_not_there(d, ['tags1', 'tags2'], list)
int()
返回0
,list()
返回一个新的空列表。
这就是标准库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']}
答案 1 :(得分: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而不仅仅是空的。如果您尝试:
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']}
如果您真的想避免导入**,可以尝试:
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])
也有效