Dictionary initializing in python - python

Is there any way to refer to the dict keys in the initialization body using one line and using dict keys "a" and "b"
Example:
def func(a,b)
return {"a":longComputation1(), "b":longComputation2(), sum_a_b:?????}
Please don't change semanthics of code. This just an example.

Use the function parameters:
>>> def func(a, b):
... return {"a": a, "b": b, "sum_a_b": a + b}
...
>>> func(1, 2)
{'a': 1, 'b': 2, 'sum_a_b': 3}
UPDATE Question changed after I posted the above code; Use jonrsharpe's solution.

Short answer: no.
This would have to be done over multiple lines:
def func():
d = {"a": longComputation1(),
"b": longComputation2()}
d.update(sum_a_b = d['a'] + d['b'])
return d

Use a function to create the dict and define the names of the keys for the sum in a key named sum:
def sum_dict(**kwargs):
result = {}
total = 0
sum_keys = kwargs["sum"]
del kwargs["sum"]
for key, value in kwargs.items():
val = value()
result[key] = val
if key in sum_keys:
total += val
result["sum_" + "_".join(sum_keys)] = total
return result
print(sum_dict(a=lambda: 3,b=lambda: 2,c=lambda: 14, sum=["a", "b"]))
# {'a': 3, 'c': 14, 'b': 2, 'sum_a_b': 5}
To access the keys from a not created dict is not possible.
Another way would be to create a own dict class.

I wonder what the practical application of this is, but if you mean that the key is dynamically constructed at initialisation time from other keys already present in the dictionary:
d = {"a":longComputation1(), "b":longComputation2()}
d['_'.join(['sum'] + d.keys())] = sum(d.values()) # assumes that keys are strings
>>> d
{'a': 10, 'b': 20, 'sum_a_b': 30} # assuming that longComputation1() == 10 and longComputation2() = 20
Sorry that it is not a single line (why the constraint??), but AFAIK you can't refer to the dict's keys during initialisation.

Related

Add Counters keeping zero values

I try to sum the value of a key present in other dictionaries with this code:
import functools
import operator
import collections
my_dict = [{'a':0, 'b':1, 'c':5}, {'b':3, 'c':2}, {'b':1, 'c':1}]
sum_key_value = functools.reduce(operator.add, map(collections.Counter, my_dict))
print(sum_key_value)
# Output
# Counter({'c': 8, 'b': 5})
My question is if I want the output to keep all dictionary keys, even if the key does not appear in all the dictionaries like a in my case, what is the best way to do that without using a loop ?
Well there's a lot of nice ways to do it with a for loop, but since you specifially want to avoid a for loop, here's one way:
sum_key_value = dict(functools.reduce(lambda a, b: a.update(b) or a,
my_dict, collections.Counter()))
So what happens here is you create a single Counter, and use it to accumulate the values.
As mentioned in the comments, adding Counter objects will remove non positive keys.
So the issue is not really about not ending up with the union of all keys (as well as adding common values), since that is indeed the behaviour, see if we set a:2:
my_dict = [{'a':2, 'b':1, 'c':5}, {'b':3, 'c':2}, {'b':1, 'c':1}]
functools.reduce(operator.add, map(Counter, my_dict))
# Counter({'a': 2, 'b': 5, 'c': 8})
However, as shown in the question, as per the current implementation when adding Counter objects, non positive values (a:0) get removed.
If you really wanted to use Counter for this, you could tweak a little the current implementation overriding __add__ to get the expected behaviour:
class Counter_tweaked(Counter):
def __add__(self, other):
if not isinstance(other, Counter):
return NotImplemented
result = Counter_tweaked()
for elem, count in self.items():
newcount = count + other[elem]
result[elem] = newcount
for elem, count in other.items():
if elem not in self:
result[elem] = count
return result
functools.reduce(operator.add, map(Counter_tweaked, my_dict))
# Counter_tweaked({'a': 0, 'b': 5, 'c': 8})
The most straightforward approach, here, would be a loop. You might have keys appearing in dictionaries anywhere in the list (e.g.: the third one could have the key "e"), so you would need at least one loop to get the total of keys. And then you can just loop through all dictionaries again to sum up the values. Make an own function of it and you can call it without ever caring about loops again.
def sum_it_up(dictlist):
outdic = {}
for d in dictlist:
for k in d.keys():
outdic[k] = 0
for d in dictlist:
for k in d.keys():
outdic[k]+=d[k]
return outdic
my_dict = [{'a':0, 'b':1, 'c':5}, {'b':3, 'c':2}, {'b':1, 'c':1}]
sum_key_value = sum_it_up(my_dict)
Subtract(or Update) a zero value Counter to keep zero value items
my_dict = [{'a':0, 'b':1, 'c':5}, {'b':3, 'c':2}, {'b':1, 'c':1}]
sum_my_dict = Counter()
zero_dict = Counter()
my_dict_keys = set()
# get the keys of all dict
for dic in my_dict:
sum_my_dict += Counter(dic)
my_dict_keys.update(dic.keys())
# create a dict with all zero values
for key in my_dict_keys:
zero_dict[key] = 0
# in-place subtract zero dict (alter sum_my_dict)
sum_my_dict.subtract(zero_dict)
# in-plact update is the same with subtract
# sum_my_dict.update(zero_dict)
print(sum_my_dict)
Counter({'c': 8, 'b': 5, 'a': 0})

Creating a dict from a set of variables using their names [duplicate]

I quite regularly want to create a dictionary where keys are variable names. For example if I have variables a and b I want to generate: {"a":a, "b":b} (typically to return data at the end of a function).
Are there any (ideally built in) ways in python to do this automatically? i.e to have a function such that create_dictionary(a,b) returns {"a":a, "b":b}
Have you considered creating a class? A class can be viewed as a wrapper for a dictionary.
# Generate some variables in the workspace
a = 9; b = ["hello", "world"]; c = (True, False)
# Define a new class and instantiate
class NewClass(object): pass
mydict = NewClass()
# Set attributes of the new class
mydict.a = a
mydict.b = b
mydict.c = c
# Print the dict form of the class
mydict.__dict__
{'a': 9, 'b': ['hello', 'world'], 'c': (True, False)}
Or you could use the setattr function if you wanted to pass a list of variable names:
mydict = NewClass()
vars = ['a', 'b', 'c']
for v in vars:
setattr(mydict, v, eval(v))
mydict.__dict__
{'a': 9, 'b': ['hello', 'world'], 'c': (True, False)}
You can write your own function for create_dict
def create_dict(*args):
return dict({i:eval(i) for i in args})
a = "yo"
b = 7
print (create_dict("a", "b"))
Which gives {'a': 'yo', 'b': 7} output.
Here's a simple generator for the same:
vars = ["a", "b"]
create_dict = {i:eval(i) for i in args}
or you can use this one-liner lambda function
create_dict = lambda *args: {i:eval(i) for i in args}
print (create_dict("a", "b"))
But if you want to pass the variables to the function instead of the variable name as string, then its pretty messy to actually get the name of the variable as a string. But if thats the case then you should probably try using locals(), vars(), globals() as used by Nf4r
Extending on the code of #Nf4r, I use something like:
a, b = 1, 2
def make_dict(*args):
# Globals will change of size, so we need a copy
g = {k: v for k, v in globals().items() if not k.startswith('__')}
result = {}
for arg in args:
for k, v in g.items():
try:
if v == arg:
result[k] = v
except ValueError:
continue # objects that don't allow comparison
return result
make_dict(a, b)
Have you tried something like:
a, b, c, d = 1, 2, 3, 4
dt = {k:v for k, v in locals().items() if not k.startswith('__')}
print(dt)
{'a': 1, 'd': 4, 'b': 2, 'c': 3}

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.

Map a function to values of specified keys in dictionary

Is there a convenient way to map a function to specified keys in a dictionary?
Ie, given
d = {"a": 1, "b": 2, "c": 3}
would like to map a function, say f, to keys "a" and "c":
{"a": f(1), "b": 2, "c": f(3)}
EDIT
Looking for methods that will not update the input dictionary.
You can use a dictionary comprehension:
output_dict = {k: f(v) for k, v in d.items()}
Note that f(v) will be evaluated (called) immediately and its return values will be stored as the dictionary's values.
If you want to store the function and call it later (with the arguments already stored) you can use functools.partial:
from functools import partial
def f(n):
print(n * 2)
d = {"a": 1, "b": 2, "c": 3}
output_dict = {k: partial(f, v) for k, v in d.items()}
output_dict['b']()
# 4
If you only want specific keys mapped you can of course not use .items and just override those keys:
d['a'] = partial(f, d['a'])
or more generalized
keys = ('a', 'c')
for key in keys:
d[key] = partial(f, d[key])

Convert a list into a nested dictionary

For example I have
x = ['a','b','c']
I need to convert it to:
y['a']['b']['c'] = ''
Is that possible?
For the background, I have a config file which contains dotted notation that points to a place in some json data. I'd like to use the dotted notation string to access that specific data in the json file. For example, in the config:
path_to_data = "user.name.first_name"
I'd like my script to recognize that as:
json_data["user"]["name"]["first_name"]
so I can get the value of the first_name field. I converted the original string into a list, and now I don't know how to convert it to a nested dict.
EDIT: There is an existing data structure that I need to apply the dict with. Let's say:
m = {'a': {'b': {'c': 'lolcat'}}}
so that
m['a']['b']['c']
gives me 'lolcat'. If I get the right dictionary structure (as some of the replies did), I would still need to apply this to the existing dictionary 'm'.
So, again, I get this from a config file:
c = 'a.b.c'
That I converted to a list, thinking this will make things easier:
x = ['a','b','c']
Now I have a json-like data structure:
m = {'a': {'b': {'c': 'lolcat'}}}
So the nested dict generated from 'x' should be able to traverse 'm' so that
m['a']['b']['c']
gets me the cat.
li = ['a','b','c']
d = reduce(lambda x, y: {y:x}, reversed(li+['']))
print(d)
print(d['a']['b']['c'])
I guess you also want to include a value in the end. This works for that too:
def get_value(d, l):
if len(l) > 1:
return get_value(d[l[0]], l[1:])
return d[l[0]]
def add_keys(d, l, c=None):
if len(l) > 1:
d[l[0]] = _d = {}
d[l[0]] = d.get(l[0], {})
add_keys(d[l[0]], l[1:], c)
else:
d[l[0]] = c
def main():
d = {}
l1 = ['a', 'b', 'c', 'd']
c1 = 'letters'
l2 = [42, "42", (42,)]
c2 = 42
add_keys(d, l1, c1)
print d
add_keys(d, l2, c2)
print d
if __name__ == '__main__':
main()
It prints:
{'a': {'b': {'c': {'d': 'letters'}}}}
{'a': {'b': {'c': {'d': 'letters'}}}, 42: {'42': {(42,): 42}}}
letters
42
So it surely works. Recursion for the win.
>>> x = ['a','b','c']
>>> y={}
>>> y[x[-1]]=""
>>> x.pop(-1)
'c'
>>> for i in x[::-1]:
... y={i:y}
...
>>> y
{'a': {'b': {'c': ''}}}
>>> y['a']['b']['c']
''
This will work.
#!/usr/bin/python2
from __future__ import print_function
x = ['a','b','c']
def ltod(l):
rv = d = {}
while l:
i = l.pop(0)
d[i] = {}
d = d[i]
return rv
d = ltod(x)
print(d)
print(d["a"]["b"]["c"])
d["a"]["b"]["c"] = "text"
print(d["a"]["b"]["c"])
Outputs:
{'a': {'b': {'c': {}}}}
{}
text
Find below sample that is not very beautiful but quite simple:
path_to_data = "user.name.first_name"
keys = path_to_data.split('.')
t = []
for key in keys[::-1]: # just to iterate in reversed order
if not t:
t.append({k:{}})
else:
t[-1] = ({k: t[-1]})
#t[0] will contain your dictionary
A general solution would be to use collections.defaultdict to create a nested dictionary. Then override __setitem__ for whatever behavior you'd like. This example will do the string parsing as well.
from collections import defaultdict
class nesteddict(defaultdict):
def __init__(self):
defaultdict.__init__(self, nesteddict)
def __setitem__(self, key, value):
keys = key.split('.')
for key in keys[:-1]:
self = self[key]
defaultdict.__setitem__(self, keys[-1], value)
nd = nesteddict()
nd['a.b.c'] = 'lolcat'
assert nd['a']['b']['c'] == 'lolcat'

Categories

Resources