I'm not even able to properly search google for it, but here goes:
a = {}
b = {}
c = [a, b]
for d in c:
d['ID'] = d
print c
returns:
[{'ID': {...}}, {'ID': {...}}]
why isn't it:
[{'ID': a}, {'ID': b}]
Let's step through this:
a = {}
b = {}
c = [a, b]
So far, so good.
for d in c:
d['ID'] = d
We can unroll this to:
d = c[0]
d['ID'] = d
d = c[1]
d['ID'] = 1
And expand that to:
d = a
d['ID'] = d
d = b
d['ID'] = d
Now substitute:
a['ID'] = a
b['ID'] = a
So, let's forget about the loop for a second and look at what that does:
>>> a = {}
>>> a['ID'] = a
>>> a
{'ID': {...}}
In other words, you're making each dict recursively contain a copy of itself, under the key ID. How would you expect this to be printed?
So, the obvious thing to do is to try to print the whole dictionary:
{'ID': {'ID': {'ID': { …
But this would be an infinitely-long string, and Python would run out of stack space before reaching infinity. So it needs to truncate it somehow.
It can't print this:
{'ID': a}
Because a is just a name that happens to be bound to the dict, just like d is at the time. In fact, the loop doesn't even know that a is bound to that at the time; it knows that d is. But even if it did know, the result would be wrong. Think about this:
>>> e = a
>>> a = 0
>>> e
???
So, the obvious answer is to use an ellipsis (kind of like I did in the human-readable version) to represent "and so on".
a is a dictionary.
b is a dictionary.
c is a list of two dictionaries (not "two names" or "two variables").
Another socratic explanation:
If it would return [{'ID': a}, {'ID': b}], values displayed as a and b would be of which type?
Consider what the loop is doing:
a = {}
b = {}
c = [a, b]
for d in c:
d['ID'] = d
d will be either a or b, such that
a['ID'] = a
b['ID'] = b
but recall a and b are {}, the dics themselves. as a result, your assigning ['ID'] to the dic itself, creating a loop. When you
print(c)
you get [{'ID': {...}}, {'ID': {...}}] because the value of the key is the dic itself and not the variable representation of it, hence you get {...} to reflect the nature of the loop.
Note how after this a['ID']['ID'], or even a ['ID']['ID']['ID']['ID'] is {'ID': {...}}, because the value of the key is the dic itself, not the variable pointing to it.
for d in c:
d['ID'] = d
should be
c = [{'ID': d} for d in c]
Your code is adding the ID element to each of the dicts in c. That means a = {'ID': a} after your code has run. It contains a reference to itself.
My snippet generates a new dict with a property 'ID' containing a value from c.
Related
I'm trying to create a multiple hierarchy of nested dictionary. The hierarchy levels are separated with a dot(.) in variable B however the final key (A) and value (D) are fixed.
Variables
A = "key"
B = "one.two.three.four"
D = "value"
Desired Output
{ one : { two : {three : {four : {key: value}}}}}
Here, the length of hierarchy (variable B) might increase or decrease based on input. I'm unable to create such dynamic code.
My pseudocode Code
A = "key"
B = "one.two.three.four"
D = "value"
inner_dictionary = {}
whole_dictionary = {}
lst = B.split('.')
length = len(lst)
for i in range(length):
new = lst[-1]
tmp = {A:D}
inner_dictionary.update(tmp)
val = { new : inner_dictionary}
whole_dictionary.update(val)
lst.pop()
print(whole_dictionary)
My Output
{'four': {'key': 'value'}, 'three': {'key': 'value'}, 'two': {'key': 'value'}, 'one': {'key': 'value'}}
I need help on this. Thanks in advance!
Use this:
A = "key"
B = "one.two.three.four"
D = "value"
x = {A: D}
for k in B.split('.')[::-1]:
x = {k: x}
print(x)
Output:
{'one': {'two': {'three': {'four': {'key': 'value'}}}}}
Or, in Python 3.8+, using the walrus operator:
A = "key"
B = "one.two.three.four"
D = "value"
x = {A: D}
[(x := {k: x}) for k in B.split('.')[::-1]]
print(x)
Output:
{'one': {'two': {'three': {'four': {'key': 'value'}}}}}
Note: the second solution takes a lot more time (you can run the following code to check that):
from timeit import timeit
print(timeit("""A = "key"
B = "one.two.three.four"
D = "value"
x = {A: D}
for k in B.split('.')[::-1]:
x = {k: x}"""))
print(timeit("""A = "key"
B = "one.two.three.four"
D = "value"
x = {A: D}
[(x := {k: x}) for k in B.split('.')[::-1]]"""))
The first one takes about 0.5s, the second one about 1s.
One approach using a single for-loop:
A = "key"
B = "one.two.three.four"
D = "value"
start = { A: D }
for k in reversed(B.split(".")):
start = { k : start }
print(start)
Output
{'one': {'two': {'three': {'four': {'key': 'value'}}}}}
This type of problem where a list of values aggregates to a single one can be solved using reduce (a la functional programming):
from functools import reduce
A = "key"
B = "one.two.three.four"
D = "value"
res = reduce(lambda x, y: {y: x}, reversed(B.split(".")), {A: D})
I have several dictionaries:
a["size"] = 12
b["size"] = 10
c["size"] = 11
I need the variable name (a,b or c) with the biggest "size" entry --> How would you implement it? I coud do it with a few if statements. But is there an easier way? It would be easy (with max(..)) if I would need the biggest number.
>>> a = {'size': 12}
>>> b = {'size': 10}
>>> c = {'size': 11}
>>> l = locals()
>>> max("abc", key=lambda x: l[x]['size'])
'a'
Use code below to get dict variable name with biggest size, without knowing names of your variables:
items = locals()
biggest = 0
a = {'size': 0}
b = {'size': -1}
c = {'size': 5}
d = {'size': 10}
e = {'size': 20}
for key in items.keys():
if type(items[key]) == dict:
size = items[key].get('size')
if size is not None and items[key].get('size') > biggest:
biggest = size
name = key
print name
you can also use list comprehension:
val=max([x["size"] for x in [a,b,c]])
I have a prototype dictionary that i want to use as a basis for appending to a list.
I want to change 1 or more values in the dictionary then capture it as an element of a list.
My question is this; is there any other recommended method for doing this short of using deepcopy().
I know this doesn't work properly:
l = []
d = {}
d['key'] = 4
l.append(d)
d['key'] = 5
l.append(d)
it gives:
l = [{'key': 5}, {'key': 5}]
it also didn't seem to work using a simply copy()
You are appending a reference to the same object to both lists, so when you change the value of "key", you change it in both lists. You need to make a copy of the dictionary before appending if you want a separate reference, using the dict.copy function:
l = []
d = {}
d['key'] = 4
l.append(d.copy())
d['key'] = 5
l.append(d.copy())
If you need a deep copy, you can use the copy library:
import copy
l = []
d = {}
d['key'] = 4
l.append(copy.deepcopy(d))
d['key'] = 5
l.append(copy.deepcopy(d))
copy should work.
l = []
d = {}
d['key'] = 4
l.append(d)
d = d.copy()
d['key'] = 5
l.append(d)
Result:
[{'key': 4}, {'key': 5}]
I'm struggling to get to a nested dictionary in a list.
L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
I can get the individual dictionaries with the following code.
for a in L:
print (a)
{'color': 'yellow', 'fruit': 'banana'}
{'lastname': 'Black', 'firstname': 'Jack'}
But now I only want the dictionary with lastname/firstname.
After the tips posted to my question, I came up with the following code.
def getPhotosFromAlbum(albumName):
availablePhotos = []
availableAlbums = getAlbumList()
print("Looking in album %s" %albumName)
for album in availableAlbums:
if albumName == album['AlbumName']:
#print ("found photos for album: ", albumName['KeyList'])
for photoRefKey in album['KeyList']:
print(getPhotoPath(photoRefKey))
availablePhotos.append(getPhotoPath(photoRefKey))
return availablePhotos
The idea behind this function is that I can parse the list in a HTML template file to display the photos. So my follow up question is: is this code ok or is there a more Python like approach to achieve the same result.
Darrell.
Use a list comprehension:
only_last_and_first = [d for d in L if 'firstname' in d and 'lastname' in d]
Now you have a list of dictionaries that have those two keys.
Demo:
>>> L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
>>> [d for d in L if 'firstname' in d and 'lastname' in d]
[{'lastname': 'Black', 'firstname': 'Jack'}]
If you only wanted the first such a dictionary, use next() and a generator expression:
first = next((d for d in L if 'firstname' in d and 'lastname' in d), None)
Here first will be set to either the first dictionary with the two keys, or None if there is no such dictionary.
If such syntax is a little daunting, here is a version with a for loop instead:
first = None
for d in L:
if 'firstname' in d and 'lastname' in d:
first = d
break
I build on Martijn's answer to propose a solution:
returning a print of items, just like in your example (and not a list)
checking that keys are exactly 'lastname' and 'firstname' (not just contain)
script uses an helper to check very simply if one list is sublist of another
L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
def contains_sublist(lst, sublst):
for k in sublst:
if k not in lst:
return False
return True
for di in L:
if len(di.keys()) == 2 and contains_sublist(di.keys(),
['firstname','lastname']):
print di
returns
>>>
{'lastname': 'Black', 'firstname': 'Jack'}
////////// EDIT -- thanks Martijn
You can use .viewkeys() for increased efficiency
L = [{'color':'yellow','fruit':'banana'},{'firstname':'Jack','lastname':'Black'}]
for di in L:
if len(di) == 2 and di.viewkeys() & {'firstname','lastname'}:
print di
this one checks if intersection between both sets is non empty di.viewkeys() & {'firstname','lastname'}
When I try this code:
dict_a = dict_b = dict_c = {}
dict_c['hello'] = 'goodbye'
print(dict_a)
print(dict_b)
print(dict_c)
I expected that it would just initialise the dict_a, dict_b and dict_c dictionaries, and then assign a key in dict_c, resulting in
{}
{}
{'hello': 'goodbye'}
But it seems to have a copy-through effect instead:
{'hello': 'goodbye'}
{'hello': 'goodbye'}
{'hello': 'goodbye'}
Why?
This is because in Python, variables (names) are just references to individual objects. When you assign dict_a = dict_b, you are really copying a memory address (or pointer, if you will) from dict_b to dict_a. There is still one instance of that dictionary.
To get the desired behavior, use either the dict.copy method, or use copy.deepcopy if your dict may have nested dicts or other nested objects.
>>> a = {1:2}
>>> b = a.copy()
>>> b
{1: 2}
>>> b[3] = 4
>>> a
{1: 2}
>>> b
{1: 2, 3: 4}
>>>
Even though
>>> dict_a, dict_b, dict_c = {}, {}, {}
is the right way to go in most cases, when it get more than 3 it looks weird
Imagine
>>> a, b, c, d, e, f = {}, {}, {}, {}, {}, {}
In cases where I wanna initialize more than 3 things, I use
>>> a, b, c, d, e, f, = [dict() for x in range(6)]
Your first assignment assigns the same dictionary object to the variables dict_a, dict_b, and dict_c. It is equivalent to dict_c = {}; dict_b = dict_c; dict_a = dict_c.
As danben previously said, you're just copying the same dict into 3 variables, so that each one refers to the same object.
To get the behaviour you want, you should instantiate a different dict in each variable:
>>> dict_a, dict_b, dict_c = {}, {}, {}
>>> dict_c['hello'] = 'goodbye'
>>> print dict_a
{}
>>> print dict_b
{}
>>> print dict_c
{'hello': 'goodbye'}
>>>
I agree with what is said above. The key here is that, in Python, assignments represent references to the object.
I was trying to grasp the concept myself and I think is it important to understand in which case a new object is created and when is the existing one changed.
In the example above, the line:
dict_c['hello'] = 'goodbye'
doesn't create a new object. It only changes the object which is referenced by dict_a, dict_b, and dict_c.
If, instead, you wrote:
dict_c = {'hello': 'goodbye'}
it would create a new object which would be referenced by dict_c. Dict_a and dict_b would still be pointing to the empty object.
In that case, if you run:
print dict_a
print dict_b
print dict_c
you would get:
{}
{}
{'hello': 'goodbye'}