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

如您所见,tags1实际上tags2指的是同一个列表,这不是有意的。cnt1并且cnt2工作正常。

如何实现set_if_not_there以便创建copies可变对象,但仅在需要时?也就是说,如果默认值是“标量”(int、string、None...),则不需要副本,但对于列表和字典,则需要副本。

4

2 回答 2

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()0return ,返回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']}
于 2013-10-13T09:38:39.200 回答
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])

也有效

于 2013-10-13T09:52:29.167 回答