Generating dictionaries using a for loop - python-3.x

I am trying to build a Player class that keeps track of NFL player data. To hold weekly stats I thought I would use a dictionary (weekly_stats) that would be comprised of dictionaries that represent week 1-17 (i.e weekly_stats = {'week1': {'pass_attempts = 0', #more stats etc}, 'week2': {'pass_attempts = 0',}, # etc on to 'week17'}. There are a lot of stats and I may add some more another time so instead of copying and pasting that 17 times and manually incrementing the dictionary key, I tried to use a for loop:
def Player:
def __init__(self):
self.weekly_stats ={}
for i in range(1,17):
self.weekly_stats['week'+str(i)]: { # dict keys will be 'week1', 'week2', etc
'pass_attempts' : 0,
'completions' : 0,
#etc,
}
}
I am trying to use i to say self.weekly_stats['week1'] , self.weekly_stats['week2'], etc through each iteration.
When I create a Player object this code seems to run as an object is created and no error is thrown. However, when I try to access this weekly_stats dict:
print(players['tom_brady'].weekly_stats['week2']['pass_attempts'])
it returns KeyError: 'week2'. It seems like the dictionary keys are not being created? Can anyone help with this?

You should use the method 'update' to add a key to a dict
class Player:
def __init__(self):
self.weekly_stats = {}
for i in range(1, 17):
self.weekly_stats.update({"week" + str(i): {
"pass_attempts": 0,
"completions": 0,
}})
This article has more info on dict manipulation: https://www.geeksforgeeks.org/python-add-new-keys-to-a-dictionary/

I believe the problem is due to the fact that you are trying to access a key passing_attempts where the dictionary has a key pass_attempts instead.
The following is a suggested solution:
NUM_WEEKS = 16
class Player:
def __init__(self):
self.weekly_stats = {
f'week{i+1}': {
"pass_attempts": 0,
"completions": 0
} for i in range(NUM_WEEKS)
}
players = {'tom_brady': Player()}
print(players['tom_brady'].weekly_stats['week2']['pass_attempts'])

Related

Is it possible to swap two keys of an ordered dictionary?

Note: I am using python 3.8.2, so dictionaries are considered ordered
I was creating a binary tree, and I used dictionaries to model the tree.
For example:
{1:[2, 3], 2:[4, 5], 3:[], 4:[], 5:[]}
In the example, the tree above would look like this:
1
/ \
2 3
/ \
4 5
I was trying to simulate the ‘rising’ of some nodes, and keep the order of the dict as well.
I know that using myDict[key1], myDict[key2] = myDict[key2], myDict[key1] won’t work, as the places of the values change, not the keys.
I was also thinking of using .popitem() to remove the last value until I’m either at key1 or key2, and the keep going until I get to the other key, but this seems kinda hard. Is there any other way to do this?
Dictionaries, although they now keep the insertion order, are not arbitrarily orderable otherwise. If you want really to use the dict order information to build your tree, I think the only reliable way would be to build a fresh dictionary, copying the contents of the original one, for each of these swap operations.
A more reasonable approach, if you want an arbitrarily ordered dictionary would be to inherit from collections.abc.MutableMappingand kep track of your data inside that object, using a dictionary and some other data structure, such as a list.
It may sound complicated, but it may be simpler than you think:
from collections.abc import MutableMapping
class SuperOrdered(MutableMapping):
def __init__(self):
self.data = {}
self.order = []
def __setitem__(self, key, value):
if key not in self.data:
self.order.append(key)
self.data[key] = value
def __getitem__(self, key):
return self.data[key]
def __delitem__(self, key):
del self.data[key]
self.order.remove(key)
def __len__(self):
return len(self.data)
def __iter__(self):
yield from iter(self.order)
def replace_key(self, oldkey, newkey, value):
if newkey in self.data:
del self[newkey]
position = self.order.index(oldkey)
self.order[position] = newkey
self.data[newkey] = value
def __repr__(self):
return f"{self.__class__.__name__}({{{', '.join(repr(key) + ':' + repr(self.data[key]) for key in self)}}})"
And voilá - the mapping + the "replace_key" method should be enough for you to build your tree as you are thinking about it.
This is the class above in the interactive prompt:
In [18]: aa = SuperOrdered()
In [19]: aa["a"] = 1;aa["b"] = 2;aa["c"] = 3
In [20]: aa
Out[20]: SuperOrdered({'a':1, 'b':2, 'c':3})
In [21]: aa.replace_key("a", "d", 4)
In [22]: aa
Out[22]: SuperOrdered({'d':4, 'b':2, 'c':3})
Apart from this answer, and out of topic: if you want to check a tree implementation that I hope is "production ready", I've published one as part of my extradict package (pip installable).
update: One might also inherit from collections.OrderedDict and add a replace_key method there. That code would have to deal with OrderedDict internals, but it would not be hard.
External links:
Github modification
Since you only want to swap two keys, we could start off with the following code:
>>> changedDict = {}
>>> for key, value in myDict.items():
... if key not in (key1, key2):
... changedDict[key] = value
And continue as follows:
... elif key == key1:
... changedDict[key] = myDict[key2]
... else:
... changedDict[key] = myDict[key1]

Accessing dynamically created Variables set in a for loop

TLDR FIX: put your objects(or list of objects) into a dictionary
Simply put, i am creating a bunch of pages in a wxpython notebook dynamically and on each page i am placing further widgets and sizers and such. my issue is that if i want to access a variable set in the for loop when it creates the page, i am unable to because once the for loops runs again the variable will be reset and used to populate the new page.
something along the lines of this
class MyFrame(wx.Frame):
def __init__(self, *args, **kwds):
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE
wx.Frame.__init__(self, *args, **kwds)
self.SetSize((400, 300))
self.notebook_1 = wx.Notebook(self, wx.ID_ANY)
self.text_button = wx.Button(self, wx.ID_ANY, "get text")
self.Bind(wx.EVT_BUTTON, self.get_text_handler, self.text_button)
self.__set_properties()
sizer_1 = wx.BoxSizer(wx.VERTICAL)
sizer_2 = wx.BoxSizer(wx.HORIZONTAL)
sizer_1.Add(self.text_button, 0, 0, 0)
sizer_1.Add(self.notebook_1, 1, wx.EXPAND, 0)
self.SetSizer(sizer_1)
self.Layout()
numlist = (1, 2, 3, 4)
#global page_dictionary
#page_dictionary = {}
for i in numlist:
self.notebook_1_pane_2 = wx.Panel(self.notebook_1, wx.ID_ANY)
self.notebook_1_pane_2.SetSizer(sizer_2)
self.notebook_1.AddPage(self.notebook_1_pane_2, str(i))
self.label_1 = wx.TextCtrl(self.notebook_1_pane_2, wx.ID_ANY, str(i))
sizer_2.Add(self.label_1, 0, 0, 0)
self.Layout()
#page_k = self.label_1.GetValue()
#page_k_good = page_k + str(i)
#page_dictionary[page_k_good] = i
#print(page_dictionary)
def __set_properties(self):
self.SetTitle("frame")
def get_text_handler(self, event): # wxGlade: MyFrame.<event_handler>
print(self.label_1.GetValue())
the only difference between this and what im actually running, is that the numlist variable is actually generating a random number of values dynamically
my issue is that if i want to reference any of the labels ive set in previous pages, i can't because the self.label_1 variable gets reset to the entry on the last page. so if i want to access the label on say page number 1 the label variable is set to the values of the last page created in the for loop. its like once the label is placed and populated on the wxNotebook page its just gone and what is in that widget isn't accessible anymore.
so how would i, say for example, print(self.label_1) of page 1 in the notebook when self.label_1 was rewritten to match the label in the last page generated?
was messing around with dictionaries so i commented that out.
how would i be able to click that button, and have it get the text from the boxes in current pages?
Final edit hopefully:
how would i go about assigning multiple objects into the dictionary? would i need to just assign the value i as a list (label_1, Label_2) then when needing to call methods on them just reference them from the list, that is now in the dictionary?
yup, that did it, put them into the dictionary as a list, can pull the list when needed.
I am still at a loss as to exactly what you are trying to do, but based on an educated guess I think you are trying to preserve the identity of objects created in a loop so that they maybe accessed outside of the loop. With this in mind, here is an example of how this can be done. Here is a very simplistic example:
import random as rnd #used to create a random number for each object
# just a list of names to be scanned in for loop
lst = ["a", "b", "c", "d"]
# Just an illustrative object for demo purposes
class SillyClass:
def __init__(self, name, nmb):
self._id = name
self._nmb = nmb
#property
def name(self):
return self._id
#property
def number(self):
return self._nmb
# create a list of objects for later reference
def build_objects(lst):
rslt = []
for itm in lst:
rslt.append(SillyClass(itm, rnd.randrange(5, 200)))
return rslt
# show use of list to print object data
obj_lst = build_objects(lst)
# Show the contents of obj_lst
print(obj_lst, "\n")
# show access of object attributes
for obj in obj_lst:
print(f"Object: {obj.name} contains {obj.number}")
The result of running this code is:
[<__main__.SillyClass object at 0x0000022C5CB9E9D0>, <__main__.SillyClass object at 0x0000022C5CB9EFD0>, <__main__.SillyClass object at 0x0000022C5CB9E670>, <__main__.SillyClass object at 0x0000022C5CC42370>]
Object: a contains 187
Object: b contains 164
Object: c contains 97
Object: d contains 52
This illustrates that obj_lst contains a list of objects, which then can be used to access the attributes of the object. You can utilize this approach or possibly associate the object to a specific identifier using a dict structure. What ever you need to best serve your purposes.

How to convert python dict to DictRow object

Hi I am writing unittest using pytest. But I am not able to mock few db functions. We are using psycopg2 for db connections and executions. Response of query returned from psycopg2 is of the type DictRow which can be accessed either by key or by index.
Ex:
response = ['prajwal', '23', 'engineer'] #Response of a query "select name, age , job from users"
>>>response[0]
'prajwal'
>>>response['name']
'prajwal'
I want to know is there any way by which we can covert dict/list to above mentioned type.
Looking at the source for psycopg2, creating a DictRow requires passing in a DictCursor object. However the only thing it uses from DictCursor appears to be an index and description attribute.
# found in lib\site-packages\psycopg2.extras.py
class DictRow(list):
"""A row object that allow by-column-name access to data."""
__slots__ = ('_index',)
def __init__(self, cursor):
self._index = cursor.index
self[:] = [None] * len(cursor.description)
The index looks like a dict with a mapping a key to an index. e.g.response['name'] = 0
The description looks like your dict that you want to convert.
If you're feeling hacky you could take advantage of duck typing and pretend you're passing in a cursor when you're just satisfying the requirements.
The only caveat is after we instantiate the DictRow, we need to populate it. Our fake cursor hack will take care of the rest.
from psycopg2.extras import DictRow
class DictRowHack:
def __init__(self, my_dict):
# we need to set these 2 attributes so that
# it auto populates our indexes
self.index = {key: i for i, key in enumerate(my_dict)}
self.description = my_dict
def dictrow_from_dict(my_dict):
# this is just a little helper function
# so you don't always need to go through
# the steps to recreate a DictRow
fake_cursor = DictRowHack(my_dict)
my_dictrow = DictRow(fake_cursor)
for k, v in my_dict.items():
my_dictrow[k] = v
return my_dictrow
response = {'name': 'prajwal', 'age': '23', 'job': 'engineer'}
my_dictrow = dictrow_from_dict(response)
print(my_dictrow[1])
print(my_dictrow['name'])
print(type(my_dictrow))

How to add a set of multiple values to a key?

I created a class which is basically a hobby book. The book can be accessed by two methods, enter(n,h) which takes a name and keep adding hobbies to that name(one name can have multiple hobbies). The other method returns a set of hobbies for a particular name. My hobby book is storing every hobby that I insert to one name. Can someone help me fixing it?
class Hobby:
def __init__(self):
self.dic={}
self.hby=set()
def enter(self,n,h):
if n not in self.dic.items():
self.dic[n]=self.hby
for k in self.dic.items():
self.hby.add(h)
def lookup(self,n):
return self.dic[n]
I tried running following cases
d = Hobby(); d.enter('Roj', 'soccer'); d.lookup('Roj')
{'soccer'}
d.enter('Max', 'reading'); d.lookup('Max')
{'reading', 'soccer'} #should return just reading
d.enter('Roj', 'music'); d.lookup('Roj')
{'reading', 'soccer','music'} #should return soccer and music
Why are you re-inventing a dict here? Why are you using a separate set to which you always add values, and reference it to every key which ensures that it always returns the same set on a lookup?
Don't reinvent the wheel, use collections.defaultdict:
import collections
d = collections.defaultdict(set)
d["Roj"].add("soccer")
d["Roj"]
# {'soccer'}
d["Max"].add("reading")
d["Max"]
# {'reading'}
d["Roj"].add("music")
d["Roj"]
# {'soccer', 'music'}
.
UPDATE - If you really want to do it through your own class (and before you do, watch Stop Writing Classes!), you can do it as:
class Hobby(object):
def __init__(self):
self.container = {}
def enter(self, n, h):
if n not in self.container:
self.container[n] = {h}
else:
self.container[n].add(h)
def lookup(self, n):
return self.container.get(n, None)
d = Hobby()
d.enter("Roj", "soccer")
d.lookup("Roj")
# {'soccer'}
d.enter("Max", "reading")
d.lookup("Max")
# {'reading'}
d.enter("Roj", "music")
d.lookup("Roj")
# {'soccer', 'music'}
Note how no extra set is used here - every dict key gets its own set to populate.

Iterating through class variables in python

Please correct my code
PS - i'm fairly new to python
class Contact:
def __init__(self,cid, email):
self.cid=cid
self.email=email
def ind(contacts):
index={}
#Code here
return index
contacts = [Contact(1,'a'),
Contact(2,'b'),
Contact(3,'c'),
Contact(4,'a')]
print(ind(contacts))
Need the output to be like -
{'a':[1,4], 'b':2, 'c':3}
The following methods create list values like:
{'a':[1,4], 'b':[2], 'c':[3]}
I can't imagine why this wouldn't be fine, but I've added a method at the end that gets your specific output.
This doesn't maintain order of the emails:
def ind(contracts):
index={}
for contract in contracts:
index.setdefault(contract.email, []).append(contract.cid)
return index
To maintain order (e.g. start with 'a'), add from collects import OrderedDict to the top of your file and then the method is:
def ind(contracts):
index = OrderedDict()
for contract in contracts:
index.setdefault(contract.email, []).append(contract.cid)
return index
The printout of index will look different, but it acts the same as a normal dict object (just with ordering).
Exact output (with ordering):
def ind(contracts):
index = OrderedDict()
for contract in contracts:
if contract.email in index:
value = index[contract.email]
if not isinstance(value, list):
index[contract.email] = [value]
index[contract.email].append(contract.cid)
else:
index[contract.email] = contract.cid
return index

Resources