Python defining iterators for dictionary comprehension for a class - python-3.x

I want to check with someone if I defined the iteration function properly. To explain, please consider the following example:
x=[{'n':'foo', 'a': [1,2,3], 'b':[2,3,5]}, {'n':'baz','a':[4,5,6], 'b':[7,8,9]},
{'n':'foo', 'a': [4,3,4], 'b':[1,5,6]}, {'n':'bar','a':[1,2,2], 'b':[2,5,6]}]
quick_dict = {key['n']: [sample['a'] for sample in x if sample['n']==key['n']] for key in x}
This works as expected and outputs:
{'foo': [[1, 2, 3], [4, 3, 4]], 'baz': [[4, 5, 6]], 'bar': [[1, 2, 2]]}
I am trying to do something similar for a class I defined using the __next__ and __iter__ methods. The class instance has many functions and attributes but for the purpose of this question, only the attribute samples is important because it is a list of dictionaries exactly as in the above example. I defined the methods as follows:
def __next__(self):
if self.itercounter < len(self.samples)-1:
self.itercounter +=1
return self.samples[self.itercounter]
else:
raise StopIteration
def __iter__(self):
self.itercounter = -1
return self
This seems to work for list comprehensions, but it fails for dictionary comprehensions.
If I do:
quick_dict = {key['Name']: [sample['CM'] for sample in data if sample['Name'] == key['Name']]
for key in data.samples}
then it works because it is directly accessing the list of dictionaries and it knows what to do. On the other hand if I do
quick_dict = {key['Name']: [sample['CM'] for sample in data if sample['Name'] == key['Name']]
for key in data}
then it is going through my functions, and it doesn't work. It just returns a dictionary with a single key. Here 'CM' is just a key like 'a' in the example.
What am I doing wrong in my definition of __iter__ and __next__?

Your second definition of quick_dict iterates over data with for sample in data while already iterating over it with for key in data. However, your __iter__ and __next__ implementation uses a single instance attribute to control iteration, meaning that nested iteration over data won't work because the second (nested) call to __iter__ resets the counter. To support nested iteration, eliminate __next__ and have __iter__ return a generator instead:
def __iter__(self):
i = -1
while i < len(self.samples)-1:
i += 1
yield self.samples[i]

Related

Python function removing items form list, dropping unexpected elements

It's a simple code practice challenge that asks that I make a function that takes a list of mixed types and returns only the integers
def return_only_integer(lst):
for i in lst:
if type(i) != int:
lst.remove(i)
return lst
That's it, it seems simple enough but the tests are coming back negative:
return_only_integer([9, 2, "space", "car", "lion", 16])
Returns: [9, 2, 'car', 16]
return_only_integer(["hello", 81, "basketball", 123, "fox"])
Returns what it should: [81, 123]
return_only_integer([10, "121", 56, 20, "car", 3, "lion"])
Also returns what it should: [10, 56, 20, 3]
but:
return_only_integer(["String", True, 3.3, 1])
Returns: [True, 1]
The code is so simple and straightforward, I have no idea why these 2 tests are failing.
Why would 'car' even be in the first list but the other strings not?
type(True) is bool, why is it there?
This is probably due to you modifying the list in the conditional. By removing an item from the list, you are likely shifting the iteration in that operation.
It may be worth looking into filter() instead.
https://docs.python.org/3/library/functions.html#filter
You can create a temporary list inside your function to hold the items that are integers. Once you have processed all the items, you can return the temporary list as part of your return statement. If there are no integers, you can return None.
def return_only_integer(lst):
int_lst = []
for i in lst:
if type(i) == int:
int_lst.append(i)
return int_lst if int_lst else None
print (return_only_integer([9, 2, "space", "car", "lion", 16]))
print (return_only_integer(['ball', True, "space", "car", "lion", 'fish']))
This will output as follows:
[9, 2, 16]
None
def return_only_integer(lst):
for i in lst:
if type(i) != int:
lst.remove(i)
return lst
THIS FUNCTION HAS VERY BIG FAULT.
consider this list [9, 2, "space", "car", "lion", 16]
when it had removed "space" then your i had directly reached to lion and it ignored car because your list is changed and your i index is not changed. so it is moving as it is.
after removing one non integer, you must make sure to change the index position of i. so try this code. it will work.
def return_only_integer(lst):
for i in lst:
#print(i,type(i))
if type(i) != int:
#print("flag this is not integer ",i)
lst.remove(i)
return_only_integer(lst)
return(lst)
print(return_only_integer(["hello", 81, "basketball", 123, "fox"]))
hope u understand. if you didn't understand then tell me .
Indeed you should not change the list you are iterating on, it produces unexpected results. Item deleted leaves room for the next, without the latter being picked in the iteration (so not being dropped in your example).
The possible choices to perform such task involve the usage of another list. A possible solution is very familiar to python developers:
def return_only_integer(lst):
return [i for i in lst if type(i) == int]

Python Dictionary - Combine Dictionaries

Given a list of Dictionaries, return a new Dictionary of all of their keys combined.
This is what I have done so far:
def combine_dictionaries(dictionary_list):
# your code goes here
my_dictionary = {}
for key in dictionary_list:
my_dictionary.update(key, dictionary_list[key])
return my_dictionary
This is the error it produces:
list indices must be integers or slices, not dict
Can someone let me know, how to get a integer when I have been provided a list of dictionaries?
The expected result should look something like this:
{'a': 3, 'b': 2, 'c': 4, 4: 4, 3: 3}
Your function is almost there.
I believe that you should only pass key in your dictionnary update because the update built-in function accepts either another dictionary object or an iterable of key/value pairs.
def combine_dictionaries(dictionary_list):
my_dictionary = {}
for key in dictionary_list:
my_dictionary.update(key)
return my_dictionary

get self. values from class B to variables in function in class A, and then change the variable values without changing self. values in class B

I want to use values in class B(self.array_B) and assign them to variables(array_A) in class A while executing "step" function in class A. However, after I change the variable values(array_A) to be zeros in class A, the values of (self.array_B) are also changed to zeros which is very awkward. (self.array_B) should remain the same after I change the variable values in class A. Is there any way to solve this?
import numpy as np
class A:
def __init__(self):
self.B = B()
def step(self):
print("array_B:", self.B.array_B)
array_A = np.zeros((2, 2))
array_A = self.B.array_B
print("array_A:", array_A)
for i in range(2):
for j in range(2):
array_A[i][j] = 0
print("------------------")
print("after changing variable value:array_B:", self.B.array_B)
print("after changing variable value:array_A:", array_A)
return "done"
class B:
def __init__(self):
self.array_B = [[1, 2], [3, 4]]
def test_main():
env = A()
s = env.step()
print(s)
if __name__ == "__main__":
test_main()
output:
array_B: [[1, 2], [3, 4]]
array_A: [[1, 2], [3, 4]]
------------------
after changing variable value:array_B: [[0, 0], [0, 0]]
after changing variable value:array_A: [[0, 0], [0, 0]]
done
When assigning the list here:
array_A = self.B.array_B
you are only copying a reference to the original list. So A.array_A and B.array_B actually refer to the same list, and any changes to the list will be reflected in both references.
You can copy the list itself instead by using:
array_A = self.B.array_B.copy()
Now A.Array_A and B.Array_B refer to different lists, and can be changed independently.
If the list contains mutable objects itself, a simple copy() is not enough. Both lists will still contain references to the same mutable objects inside. In this case, a deepcopy() is needed, which also makes a copy of all elements inside the list:
import copy
array_A = copy.deepcopy(self.B.array_B)
This is quite an expensive operation and should only be used when needed.
just assign a copy of arrayB to arrayA:
array_A = self.B.array_B.copy()
This is because while arrayB is assigned to arrayA, it is the address of arrayB not the actual value of it is assigned to the name arrayA. Therefore, just use the method copy() to create a copy of arrayB and then assign.
The solution here would be to use deepcopy
Why does this happen?
Lists are mutable objects, which mean that array_A still points to the same object in memory that array_B.
If you had worked with a list of immutable values (like integers), a simple
array_A = list(self.B.array_B)
or even
array_A = self.B.array_B[:]
would do the trick, because it would force python to instantiate a new list.
But here, the items of array_B also are lists, so if you did that:
array_A = list(self.B.array_B)
array_A[0][0] = 3
print(self.B.array_B[0][0] == 3) # >> would print True

How to assing values to a dictionary

I am creating a function which is supposed to return a dictionary with keys and values from different lists. But I amhavin problems in getting the mean of a list o numbers as values of the dictionary. However, I think I am getting the keys properly.
This is what I get so far:
def exp (magnitudes,measures):
"""return for each magnitude the associated mean of numbers from a list"""
dict_expe = {}
for mag in magnitudes:
dict_expe[mag] = 0
for mea in measures:
summ = 0
for n in mea:
summ += n
dict_expe[mag] = summ/len(mea)
return dict_expe
print(exp(['mag1', 'mag2', 'mag3'], [[1,2,3],[3,4],[5]]))
The output should be:
{mag1 : 2, mag2: 3.5, mag3: 5}
But what I am getting is always 5 as values of all keys. I thought about the zip() method but im trying to avoid it as because the it requieres the same length in both lists.
An average of a sequence is sum(sequence) / len(sequence), so you need to iterate through both magnitudes and measures, calculate these means (arithmetical averages) and store it in a dictionary.
There are much more pythonic ways you can achieve this. All of these examples produce {'mag1': 2.0, 'mag2': 3.5, 'mag3': 5.0} as result.
Using for i in range() loop:
def exp(magnitudes, measures):
means = {}
for i in range(len(magnitudes)):
means[magnitudes[i]] = sum(measures[i]) / len(measures[i])
return means
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))
But if you need both indices and values of a list you can use for i, val in enumerate(sequence) approach which is much more suitable in this case:
def exp(magnitudes, measures):
means = {}
for i, mag in enumerate(magnitudes):
means[mag] = sum(measures[i]) / len(measures[i])
return means
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))
Another problem hides here: i index belongs to magnitudes but we are also getting values from measures using it, this is not a big deal in your case if you have magnitudes and measures the same length but if magnitudes will be larger you will get an IndexError. So it seems to me like using zip function is what would be the best choice here (actually as of python3.6 it doesn't require two lists to be the same length, it will just use the length of shortest one as the length of result):
def exp(magnitudes, measures):
means = {}
for mag, mes in zip(magnitudes, measures):
means[mag] = sum(mes) / len(mes)
return means
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))
So feel free to use the example which suits your requirements of which one you like and don't forget to add docstring.
More likely you don't need such pythonic way but it can be even shorter when dictionary comprehension comes into play:
def exp(magnitudes, measures):
return {mag: sum(mes) / len(mes) for mag, mes in zip(magnitudes, measures)}
print(exp(['mag1', 'mag2', 'mag3'], [[1, 2, 3], [3, 4], [5]]))

How do I create a default dictionary of dictionaries

I am trying to write some code that involves creating a default dictionary of dictionaries. However, I have no idea how to initialise/create such a thing. My current attempt looks something like this:
from collections import defaultdict
inner_dict = {}
dict_of_dicts = defaultdict(inner_dict(int))
The use of this default dict of dictionaries is to for each pair of words that I produce from a file I open (e.g. [['M UH M', 'm oo m']] ), to set each segment of the first word delimited by empty space as a key in the outer dictionary, and then for each segment in the second word delimited by empty space count the frequency of that segment.
For example
[['M UH M', 'm oo m']]
(<class 'dict'>, {'M': {'m': 2}, 'UH': {'oo': 1}})
Having just run this now it doesn't seem to have output any errors, however I was just wondering if something like this will actually produce a default dictionary of dictionaries.
Apologies if this is a duplicate, however previous answers to these questions have been confusing and in a different context.
To initialise a defaultdict that creates dictionaries as its default value you would use:
d = defaultdict(dict)
For this particular problem, a collections.Counter would be more suitable
>>> from collections import defaultdict, Counter
>>> d = defaultdict(Counter)
>>> for a, b in zip(*[x.split() for x in ['M UH M', 'm oo m']]):
... d[a][b] += 1
>>> print(d)
defaultdict(collections.Counter,
{'M': Counter({'m': 2}), 'UH': Counter({'oo': 1})})
Edit
You expressed interest in a comment about the equivalent without a Counter. Here is the equivalent using a plain dict
>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> for a, b in zip(*[x.split() for x in ['M UH M', 'm oo m']]):
... d[a][b] = d[a].get(b, 0) + 1
>>> print(d)
defaultdict(dict, {'M': {'m': 2}, 'UH': {'oo': 1}})
You also could a use a normal dictionary and its setdefault method.
my_dict.setdefault(key, default) will look up my_dict[key] and ...
... if the key already exists, return its current value without modifying it, or ...
... assign the default value (my_dict[key] = default) and then return that.
So you can call my_dict.setdefault(key, {}) always when you want to get a value from your outer dictionary instead of the normal my_dict[key] to retrieve either the real value assigned with this key if it#s present, or to get a new empty dictionary as default value which gets automatically stored into your outer dictionary as well.
Example:
outer_dict = {"M": {"m": 2}}
inner_dict = d.setdefault("UH", {})
# outer_dict = {"M": {"m": 2}, "UH": {}}
# inner_dict = {}
inner_dict["oo"] = 1
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1}}
# inner_dict = {"oo": 1}
inner_dict = d.setdefault("UH", {})
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1}}
# inner_dict = {"oo": 1}
inner_dict["xy"] = 3
# outer_dict = {"M": {"m": 2}, "UH": {"oo": 1, "xy": 3}}
# inner_dict = {"oo": 1, "xy": 3}
This way you always get a valid inner_dict, either an empty default one or the one that's already present for the given key. As dictionaries are mutable data types, modifying the returned inner_dict will also modify the dictionary inside outer_dict.
The other answers propose alternative solutions or show you can make a default dictionary of dictionaries using d = defaultdict(dict)
but the question asked how to make a default dictionary of default dictionaries, my navie first attempt was this:
from collections import defaultdict
my_dict = defaultdict(defaultdict(list))
however this throw an error: *** TypeError: first argument must be callable or None
so my second attempt which works is to make a callable using the lambda key word to make an anonymous function:
from collections import defaultdict
my_dict = defaultdict(lambda: defaultdict(list))
which is more concise than the alternative method using a regular function:
from collections import defaultdict
def default_dict_maker():
return defaultdict(list)
my_dict = defaultdict(default_dict_maker)
you can check it works by assigning:
my_dict[2][3] = 5
my_dict[2][3]
>>> 5
or by trying to return a value:
my_dict[0][0]
>>> []
my_dict[5]
>>> defaultdict(<class 'list'>, {})
tl;dr
this is your oneline answer my_dict = defaultdict(lambda: defaultdict(list))

Resources