How to initialize a dict from a SimpleNamespace? - python

It has been asked: How to initialize a SimpleNamespace from a dict?
My question is for the opposite direction. How to initialize a dict from a SimpleNamespace?

from types import SimpleNamespace
sn = SimpleNamespace(a=1, b=2, c=3)
vars(sn)
# returns {'a': 1, 'b': 2, 'c': 3}
sn.__dict__
# also returns {'a': 1, 'b': 2, 'c': 3}

The straightway dict wont work for heterogeneous lists.
import json
sn = SimpleNamespace(hetero_list=['aa', SimpleNamespace(y='ll')] )
json.loads(json.dumps(sn, default=lambda s: vars(s)))
This is the only way to get back the dict.

Related

Add value of the already existing key in dict in a form of container in Python

Is there any built-in function that would do the following?
dictionary = {‘a’:1, ‘b’:2, ‘c’:3}
dictionary.update(c=10)
# what happens
dictionary ---- {‘a’:1, ‘b’:2, ‘c’:10}
# what I want to happen:
dictionary ---- {‘a’:1, ‘b’:2, ‘c’:(3, 10)}
By default if keys are the same, later key would override earlier one.
If the key is already present in dict, the value of the new key: value pair would be added to already existing value in a form of container, like tuple, or list or set.
I can write a helper function to do so but I believe it should be something built-in for this matter.
You can do this
from collections import defaultdict
d = defaultdict(list)
d["a"].append(1)
d["b"].append(2)
d["c"].append(3)
d["c"].append(10)
print(d)
Result
defaultdict(list, {'a': [1], 'b': [2], 'c': [3, 10]})
Your desired solution is not very elegant, so I am going to propose an alternative one.
Tuples are immutable. Let's use lists instead, because we can easily append to them.
The data type of the values should be consistent. Use lists in any case, even for single values.
Let's use a defaultdict such that we don't have to initialize lists manually.
Putting it together:
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> for v, k in enumerate('abc', 1):
... d[k].append(v)
...
>>> d
defaultdict(<class 'list'>, {'a': [1], 'b': [2], 'c': [3]})
>>> d['c'].append(10)
>>> d
defaultdict(<class 'list'>, {'a': [1], 'b': [2], 'c': [3, 10]})
You could rewrite the update function by creating a new class:
In Python bulitins.py:
def update(self, E=None, **F): # known special case of dict.update
"""
D.update([E, ]**F) -> None. Update D from dict/iterable E and F.
If E is present and has a .keys() method, then does: for k in E: D[k] = E[k]
If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v
In either case, this is followed by: for k in F: D[k] = F[k]
"""
pass
So I write this(Inherit from UserDict, suggested by #timgeb):
from collections import UserDict
class CustomDict(UserDict):
def __init__(self):
super().__init__()
def update(self, E=None, **F) -> None:
if E:
if isinstance(E, dict):
for k in E:
self[k] = E[k]
else:
for k, v in E:
self[k] = v
else:
if isinstance(F, dict):
for key in F:
if isinstance(self[key], list):
self[key].append(F[key])
else:
self[key] = [self[key], F[key]]
dictionary = CustomDict()
dictionary.update({'a': 1, 'b': 2, 'c': 3})
print(dictionary)
dictionary.update(a=3)
print(dictionary)
dictionary.update(a=4)
print(dictionary)
Result:
{'a': 1, 'b': 2, 'c': 3}
{'a': [1, 3], 'b': 2, 'c': 3}
{'a': [1, 3, 4], 'b': 2, 'c': 3}
Maybe there are some logic errors in my code,but welcome to point out.
Perhaps you could use something like:
dictionary = {'a':1, 'b':2, 'c':3}
dictionary.update({'c': 10 if not dictionary.get('c') else tuple([dictionary['c'],] + [10,])})
# {'a': 1, 'b': 2, 'c': (3, 10)}
But it should probably be wrapped into a function to make things clean. The general pattern would be (I suppose, based on your question):
dict = {...}
if 'a' not in dict:
do_this() # just add it to the dict?
else:
do_that() # build a tuple or list?
In your above question you're mixing types -- I'm not sure if you want that, a more pythonic approach might be to have all the values as list and use a defaultdict.

What does defaultdict(list, {}) or dict({}) do?

I saw this online and I'm confused on what the second argument would do:
defaultdict(list, {})
Looking at what I get on the console, it seems to simply create a defaultdict where values are lists by default. If so, is this exactly equivalent to running defaultdict(list)?
From I read online:
The first argument provides the initial value for the default_factory attribute; it defaults to None. All remaining arguments are treated the same as if they were passed to the dict constructor, including keyword arguments.
which also makes me wonder about the difference between:
my_dict = dict({})
my_dict = dict()
the argument to the dict class in python is the instantiation values.. so passing an {} creates an empty dictionary.
Its the same case with defaultdict, except that the first argument is the default type of the values for every key.
dict({...}) just makes a dict:
>>> dict({'a': 1, 'b': 2})
{'a': 1, 'b': 2}
Which is equal to this:
>>> dict(a=1, b=2)
{'a': 1, 'b': 2}
or
>>> {'a': 1, 'b': 2}
{'a': 1, 'b': 2}
The same applies for defualtdict.

How to merge two or more dict into one dict with retaining multiple values of same key as list?

I have two or more dictionary, I like to merge it as one with retaining multiple values of the same key as list. I would not able to share the original code, so please help me with the following example.
Input:
a= {'a':1, 'b': 2}
b= {'aa':4, 'b': 6}
c= {'aa':3, 'c': 8}
Output:
c= {'a':1,'aa':[3,4],'b': [2,6], 'c': 8}
I suggest you read up on the defaultdict: it lets you provide a factory method that initializes missing keys, i.e. if a key is looked up but not found, it creates a value by calling factory_method(missing_key). See this example, it might make things clearer:
from collections import defaultdict
a = {'a': 1, 'b': 2}
b = {'aa': 4, 'b': 6}
c = {'aa': 3, 'c': 8}
stuff = [a, b, c]
# our factory method is the list-constructor `list`,
# so whenever we look up a value that doesn't exist, a list is created;
# we can always be sure that we have list-values
store = defaultdict(list)
for s in stuff:
for k, v in s.items():
# since we know that our value is always a list, we can safely append
store[k].append(v)
print(store)
This has the "downside" of creating one-element lists for single occurences of values, but maybe you are able to work around that.
Please find below to resolve your issue. I hope this would work for you.
from collections import defaultdict
a = {'a':1, 'b': 2}
b = {'aa':4, 'b': 6}
c={'aa':3, 'c': 8}
dd = defaultdict(list)
for d in (a,b,c):
for key, value in d.items():
dd[key].append(value)
print(dd)
Use defaultdict to automatically create a dictionary entry with an empty list.
To process all source dictionaries in a single loop, use itertools.chain.
The main loop just adds a value from the current item, to the list under
the current key.
As you wrote, for cases when under some key there is only one item,
you have to generate a work dictionary (using dictonary comprehension),
limited to items with value (list) containing only one item.
The value of such item shoud contain only the first (and only) number
from the source list.
Then use this dictionary to update d.
So the whole script can be surprisingly short, as below:
from collections import defaultdict
from itertools import chain
a = {'a':1, 'b': 2}
b = {'aa':4, 'b': 6}
c = {'aa':3, 'c': 8}
d = defaultdict(list)
for k, v in chain(a.items(), b.items(), c.items()):
d[k].append(v)
d.update({ k: v[0] for k, v in d.items() if len(v) == 1 })
As you can see, the actual processing code is contained in only 4 (last) lines.
If you print d, the result is:
defaultdict(list, {'a': 1, 'b': [2, 6], 'aa': [4, 3], 'c': 8})

Dict inside defaultdict being shared across keys

I have a dictionary inside a defaultdict. I noticed that the dictionary is being shared across keys and therefore it takes the values of the last write. How can I isolate those dictionaries?
>>> from collections import defaultdict
>>> defaults = [('a', 1), ('b', {})]
>>> dd = defaultdict(lambda: dict(defaults))
>>> dd[0]
{'a': 1, 'b': {}}
>>> dd[1]
{'a': 1, 'b': {}}
>>> dd[0]['b']['k'] = 'v'
>>> dd
defaultdict(<function <lambda> at 0x7f4b3688b398>, {0: {'a': 1, 'b': {'k': 'v'}}, 1:{'a': 1, 'b': {'k': 'v'}}})
>>> dd[1]['b']['k'] = 'v2'
>>> dd
defaultdict(<function <lambda> at 0x7f4b3688b398>, {0: {'a': 1, 'b': {'k': 'v2'}}, 1: {'a': 1, 'b': {'k': 'v2'}}})
Notice that v was set to v2 for both dictionaries. Why is that? and how to change this behavior without much performance overhead?
When you do dict(defaults) you're not copying the inner dictionary, just making another reference to it. So when you change that dictionary, you're going to see the change everywhere it's referenced.
You need deepcopy here to avoid the problem:
import copy
from collections import defaultdict
defaults = {'a': 1, 'b': {}}
dd = defaultdict(lambda: copy.deepcopy(defaults))
Or you need to not use the same inner mutable objects in successive calls by not repeatedly referencing defaults:
dd = defaultdict(lambda: {'a': 1, 'b': {}})
Your values all contain references to the same object from defaults: you rebuild the outer dict, but not the inner one. Just make a function that creates a new, separate object:
def builder():
return {'a': 1, 'b': {}}
dd = defaultdict(builder)

Append a dictionary to a dictionary [duplicate]

This question already has answers here:
Python "extend" for a dictionary
(8 answers)
Closed 5 years ago.
I have two existing dictionaries, and I wish to 'append' one of them to the other. By that I mean that the key,values of the other dictionary should be made into the first dictionary. For example:
orig = {
'A': 1,
'B': 2,
'C': 3,
}
extra = {
'D': 4,
'E': 5,
}
dest = # Something here involving orig and extra
print dest
{
'A': 1,
'B': 2,
'C': 3,
'D': 4,
'E': 5
}
I think this all can be achieved through a for loop (maybe?), but is there some method of dictionaries or any other module that saves this job for me? The actual dictionaries I'm using are really big...
You can do
orig.update(extra)
or, if you don't want orig to be modified, make a copy first:
dest = dict(orig) # or orig.copy()
dest.update(extra)
Note that if extra and orig have overlapping keys, the final value will be taken from extra. For example,
>>> d1 = {1: 1, 2: 2}
>>> d2 = {2: 'ha!', 3: 3}
>>> d1.update(d2)
>>> d1
{1: 1, 2: 'ha!', 3: 3}
There are two ways to add one dictionary to another:
Update (modifies orig in place)
orig.update(extra) # Python 2.7+
orig |= extra # Python 3.9+
Merge (creates a new dictionary)
# Python 2.7+
dest = collections.ChainMap(orig, extra)
dest = {k: v for d in (orig, extra) for (k, v) in d.items()}
# Python 3
dest = {**orig, **extra}
dest = {**orig, 'D': 4, 'E': 5}
# Python 3.9+
dest = orig | extra
Caveats
Note that these operations are noncommutative. In all cases, the latter is the winner. E.g.
orig = {'A': 1, 'B': 2}
extra = {'A': 3, 'C': 3}
dest = orig | extra
# dest = {'A': 3, 'B': 2, 'C': 3}
dest = extra | orig
# dest = {'A': 1, 'B': 2, 'C': 3}
It is also important to note that only from Python 3.7 (and CPython 3.6) dicts are ordered. So, in previous versions, the order of the items in the dictionary may vary.
dict.update() looks like it will do what you want...
>> orig.update(extra)
>>> orig
{'A': 1, 'C': 3, 'B': 2, 'E': 5, 'D': 4}
>>>
Perhaps, though, you don't want to update your original dictionary, but work on a copy:
>>> dest = orig.copy()
>>> dest.update(extra)
>>> orig
{'A': 1, 'C': 3, 'B': 2}
>>> dest
{'A': 1, 'C': 3, 'B': 2, 'E': 5, 'D': 4}
Assuming that you do not want to change orig, you can either do a copy and update like the other answers, or you can create a new dictionary in one step by passing all items from both dictionaries into the dict constructor:
from itertools import chain
dest = dict(chain(orig.items(), extra.items()))
Or without itertools:
dest = dict(list(orig.items()) + list(extra.items()))
Note that you only need to pass the result of items() into list() on Python 3, on 2.x dict.items() already returns a list so you can just do dict(orig.items() + extra.items()).
As a more general use case, say you have a larger list of dicts that you want to combine into a single dict, you could do something like this:
from itertools import chain
dest = dict(chain.from_iterable(map(dict.items, list_of_dicts)))
A three-liner to combine or merge two dictionaries:
dest = {}
dest.update(orig)
dest.update(extra)
This creates a new dictionary dest without modifying orig and extra.
Note: If a key has different values in orig and extra, then extra overrides orig.
There is the .update() method :)
update([other])
Update the dictionary with the key/value pairs from other, overwriting existing keys. Return None.
update() accepts either another dictionary object or an iterable of key/value pairs (as tuples or other iterables of length two). If
keyword arguments are specified, the dictionary is then updated with
those key/value pairs: d.update(red=1, blue=2).
Changed in version 2.4: Allowed the argument to be an iterable of key/value pairs and allowed keyword arguments.
The answer I want to give is "use collections.ChainMap", but I just discovered that it was only added in Python 3.3: https://docs.python.org/3.3/library/collections.html#chainmap-objects
You can try to crib the class from the 3.3 source though: http://hg.python.org/cpython/file/3.3/Lib/collections/init.py#l763
Here is a less feature-full Python 2.x compatible version (same author): http://code.activestate.com/recipes/305268-chained-map-lookups/
Instead of expanding/overwriting one dictionary with another using dict.merge, or creating an additional copy merging both, you create a lookup chain that searches both in order. Because it doesn't duplicate the mappings it wraps ChainMap uses very little memory, and sees later modifications to any sub-mapping. Because order matters you can also use the chain to layer defaults (i.e. user prefs > config > env).

Categories

Resources