Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problems with a shared mutable?

Tags:

python

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

The output is:

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

As you can see, tags1 and tags2 are actually refering to the same list, which is not intended. cnt1 and cnt2 are working fine.

How can I implement set_if_not_there so that I create copies of the mutable, but only when needed? That is, if default value is a "scalar" (int, string, None, ...) no copy is needed, but for lists and dicts a copy is necessary.

like image 320
blueFast Avatar asked Dec 20 '22 23:12

blueFast


1 Answers

Use a factory function instead of a default value:

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

and pass in callables (like functions or lambdas, or default types):

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

int() returns 0, list() returns a new empty list.

This is what the standard library collections.defaultdict() type does as well, for example.

Demo:

>>> 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']}
like image 67
Martijn Pieters Avatar answered Dec 31 '22 21:12

Martijn Pieters