Dictionary comprehension inside insert() method not working - python

I am making a MappingList class which is a list implemented as an OrderedDict.
This is the MappingList class (some methods omitted):
class MappingList(MutableSequence):
"""
A MappingList is a regular list implemented as a dictionary
"""
def __repr__(self):
return str(list(self.seq.values()))
def __getitem__(self, item):
try:
return self.seq[item]
except KeyError:
_traceback_from_none(IndexError, "list index out of range")
def __setitem__(self, key, value, *, usage=None):
if key > max(self.seq.keys()) and usage != "append":
raise IndexError("list index out of range")
self.seq[key] = value
def __delitem__(self, key):
try:
del self.seq[key]
except KeyError:
_traceback_from_none(IndexError, "list index out of range")
def __len__(self):
return len(self.seq)
def __eq__(self, other):
if not isinstance(other, MappingList):
return NotImplemented
return self.seq == other.seq
#classmethod
def _dict_from_seq(cls, seq):
return OrderedDict(enumerate(seq))
def _next_available_slot(self):
return max(self.seq) + 1
def insert(self, index, value): # todo: insert() should not overwrite
"""Insert a value into the MappingList"""
if index > max(self.seq.keys()):
raise IndexError("list index out of range")
for k, v in {k: v for k, v in self.seq.items() if k > index}:
del self.seq[k]
self.seq[k + 1] = v
self[index] = value
When I try to insert an item into a MappingList, I get the following error:
File "C:\...\My Python Programs\free_time\mappinglist.py", line 103, in test_insert
self.li.insert(1, MappingList(["blah", 1, 5.8]))
File "C:\...\My Python Programs\free_time\mappinglist.py", line 85, in insert
for k, v in {k: v for k, v in self.seq.items() if k > index}:
TypeError: cannot unpack non-iterable int object
Why is this error happening? Does OrderedDict.items() return an integer?

The error doesn't happen due to that.
When you don't provide keys(), values(), items(), python iterates over the keys by default. You need to provide items() to tell python to get the keys and values.
for k, v in {k: v for k, v in self.seq.items() if k > index}.items():

Related

iterate a dict and pass as function parameter

I have this function what returns an assignment
def insertData(
Model:object,
data_entry:dict
)->any:
for k, v in data_entry.items():
if isinstance(v, list):
return getattr(Model, k).in_(v)
else:
return getattr(Model, k)== v
and then these function is called here for it to pass assign those values
def get_vehicles(
db:Session,
skip: int = 0,
limit: int= 100,
query:Query=None
)-> Union[list[Vehicle], list[None]]:
real_query = get_only_passed_values(query)
if real_query:
return db.query(Vehicle).filter(
insertData(Vehicle, real_query)
).offset(skip).limit(limit).all()
else:
return db.query(Vehicle).offset(skip).limit(limit).all()
I need to filter multiple columns in database table, but only filter the first argument passed.
e.g:
return db.query(Vehicle).filter(Vehicle.color == 'red',Vehicle.brand == 'BMW').offset(skip).limit(limit).all()

Python dictionary not adding subsequent keys after the first

Fairly new to Python and I can not figure this out. I go to add a key to a dictionary and it adds it fine. I can even update that same key with a new value, however when I go to add a second key to the dictionary, it does not add the second key value pair.
class CountedSet:
def __init__(self):
self.data = {}
def __iadd__(self,other):
if isinstance(other,int):
self.data[other] = self.data.get(other, 0) + 1
return self
elif isinstance(other,CountedSet):
#TODO::iterate through second countedSet and update self
return self
def __add__(self,obj):
for key, value in obj.data.items():
if len(self.data) == 0:
self.data[key] = value
elif self.data[key]:
self.data[key] = self.data[key] + value
else:
self.data[key] = value
return self
def __getitem__(self,item):
if item in self.data:
return self.data.get(item)
else:
return None
def __str__(self):
for key, value in self.data.items():
return("{%s,%s}" % (key,value))
a = CountedSet()
a += 17
a += 4
print(a)
This simply outputs {17,1} when I would expect to see {17,1} {4,1}
Your __str__ implementation returns on the first iteration of the for-loop:
def __str__(self):
for key, value in self.data.items():
return("{%s,%s}" % (key,value)) # here
Maybe you want something like:
def __str__(self):
return " ".join([{"{%s,%s}" % (k,v) for k, v in self.data.items()])
Or, without the comprehension:
def __str__(self):
items = []
for key, value in self.data.items():
items.append("{%s,%s}" % (key,value))
return ' '.join(items)

How can I read a dictionary with a list?

How can I read a list inside a dictionary and try to change string numbers to digits? For example:
obj = {'azul':'4','rojo':[{'rojo_a':'1','rojo_b':'2'}],'amarillo':'xxx','naranja':[{'naranja_1':'1','naranja_2':'2'}]}
I use this to change dictionary number strings to integers:
{k:int(v) if v.isdigit() else v for k,v in obj.items()}
But it doesn't work, so I was trying something like this:
for objs in obj:
if objs.isdigit():
k:int(v)
else:
for k,v in objs.items():
print k
But this fails as well.
this seems like a good problem for recursion
obj = {'azul':'4','rojo':[{'rojo_a':'1','rojo_b':'2'}],'amarillo':'xxx','naranja':[{'naranja_1':'1','naranja_2':'2'}]}
def fix_ints(obj):
if isinstance(obj,basestring):
try:
return int(obj)
except ValueError:
print "I cant Make %r an int"%obj
return obj
elif isinstance(obj,(list,tuple)):
return [fix_ints(item) for item in obj]
elif isinstance(obj,dict):
return dict((key,fix_ints(value)) for key,value in obj.items())
else:
print "I have no idea what to do with %r"%obj
new_obj = fix_ints(obj)
print new_obj
note that python does not support tail recursion so if this data structure goes very deep (greater than 1k levels of nesting) then recursion may not be appropriate ...
of coarse you can also do silly string tricks with it
import json,re
new_obj = json.loads(re.sub("\"(\d+)\"","\\1",json.dumps(obj)))
(although really you should do it like i do in my first exzample ... this second method is really just for fun)
String to number:
def int_it(obj):
if obj.isdigit():
obj = int(obj)
return obj
Dict to number (regardless of the number of nested dicts or lists):
class Convert(object):
def __init__(self, obj):
self.obj = obj
if isinstance(obj, dict):
self.handle_dict(obj)
def handle_dict(self, obj):
for key, value in obj.items():
if isinstance(value, str) and value.isdigit():
self.obj[key] = int_it(value)
elif isinstance(obj[key], list):
ins = HandleList(obj[key])
self.obj[key] = ins.obj
elif isinstance(obj[key], dict):
ins = Convert(obj.items())
self.obj[key] = ins.obj
return obj
List to numbers, regardless of the number of nested lists or dicts.
class HandleList(object):
def __init__(self, obj):
self.obj = obj
self.handle_list(obj)
def handle_list(self, obj):
for index, item in enumerate(obj):
if isinstance(item, list):
obj.index(index, [HandleList(val).obj for val in item])
elif isinstance(item, str):
obj.index(index, int_it(item))
elif isinstance(item, dict):
Convert(item)
return obj
output = Convert(values)
print(output.obj)
Returns:
{
'amarillo': 'xxx',
'naranja': [{'naranja_1': 1, 'naranja_2': 2}],
'rojo': [{'rojo_b': 2, 'rojo_a': 1}],
'azul': 4
}
Given the input:
values = {
'azul':'4',
'rojo': [
{'rojo_a':'1',
'rojo_b':'2'
}
],
'amarillo':'xxx',
'naranja': [
{'naranja_1':'1',
'naranja_2':'2'
}
]
}

Overriding a method that yields

I am new to Python 3 concepts.
The base class has following functions:
class DocumentFormatter(object):
def transform_element(self, key, value):
if isinstance(value, list):
for li, lv in enumerate(value):
for inner_k, inner_v in self.transform_element(
"%s.%s" % (key, li), lv):
yield inner_k, inner_v
elif isinstance(value, dict):
formatted = self.format_document(value)
for doc_key in formatted:
yield "%s.%s" % (key, doc_key), formatted[doc_key]
else:
# We assume that transform_value will return a 'flat' value,
# not a list or dict
yield key, self.transform_value(value)
def format_document(self, document):
def flatten(doc, path):
top_level = (len(path) == 0)
if not top_level:
path_string = ".".join(path)
for k in doc:
v = doc[k]
if isinstance(v, dict):
path.append(k)
for inner_k, inner_v in flatten(v, path):
yield inner_k, inner_v
path.pop()
else:
transformed = self.transform_element(k, v)
for new_k, new_v in transformed:
if top_level:
yield new_k, new_v
else:
yield "%s.%s" % (path_string, new_k), new_v
return dict(flatten(document, []))
In the derived class, I only want to change a bit of the transform_element function:
def transform_element(self, key, value):
if key=="cats":
yield key, self.transform_value(value)
else:
yield super().transform_element(key,value)
If the key is "cats", I want to yield using my logic. Else, I want the base class implementation to work. Am I supposed to call yield or should I call return?
you should iterate over the overridden method and yield each item of it, otherwise you will end up with a generator of a generator which is not what you need.
So your method should look like:
def transform_element(self, key, value):
if key=="cats":
yield key, self.transform_value(value)
else:
for item in super().transform_element(key,value):
yield item

Getting the return value of __setitem__() and length of dictionary

I have the following dictionary:
class Only5Items(dict):
def __setitem__(self, key, value):
if len(self) < 5:
super(Only5Items, self).__setitem__(key, value)
I want to return true, if I success to add an item. Any ideas?
I'll explain it a little more.
For example:
items = Only5Items()
items["hi1"] = "asdf1"
items["hi2"] = "asdf2"
items["hi3"] = "asdf3"
items["hi4"] = "asdf4"
items["hi5"] = "asdf5"
items["hi6"] = "asdf6"
items["hi6"] = "asdf6" won't insert. And i want to print a message about it.
edit: len(self) works. And to be more specific, i don't want to print a message about it, i want to return true/false if i success to add it. Something external to the class.
Assignment doesn't have a return value in Python and the return value of __setitem__() is ignored. Generally, you'd want to raise an exception instead:
class Only5Items(dict):
def __setitem__(self, key, value):
if len(self) < 5 or key in self: # allow reassignment of existing key
return super(Only5Items, self).__setitem__(key, value)
raise KeyError("maximum number of items (5) exceeded")
Then your client code can catch the exception:
items = Only5Items(hi1="asdf1", hi2="asdf2", hi3="asdf3", hi4="asdf4", hi5="asdf5")
try:
items["hi6"] = "asdf6"
except KeyError as e:
print(e)
If you want to return True/False then you have to write your own assignment method that can return a value:
class Only5Items(dict):
def __setitem__(self, key, value):
if len(self) < 5 or key in self: # allow reassignment of existing key
return super(Only5Items, self).__setitem__(key, value)
raise KeyError("maximum number of items (5) exceeded")
def did_set(self, key, value):
try:
self[key] = value
except KeyError:
return False
return True
Then you'd use it like this:
if not items.did_set("hi6", "asdf6"):
print "couldn't set key 'hi6', dictionary is probably full"
You probably also want to override setdefault() to check the number of items too... also, it'd be nice (and pretty easy) to pass in the maximum number when you instantiate the class instead of hard-coding it to 5.
Erm, I'd just do the following:
class Only5Items(dict):
def __setitem__(self, key, value):
if len(self) < 5:
return super(Only5Items, self).__setitem__(key, value)
else:
raise KeyError("You forgot I can only have 5 items dummy!")
def __str__(self):
return super(Only5Items, self).__str__()
The following script:
d = Only5Items(a=1, b=2, c=3, d=4, e=5)
print d
try:
d['making a mistake'] = 'hahahahah'
except KeyError, e:
print e
Produces:
{'a': 1, 'c': 3, 'b': 2, 'e': 5, 'd': 4}
'You forgot I can only have 5 items dummy!'
class Only5Items(dict):
def __setitem__(self, key, value):
if len(self) < 5:
super(Only5Items, self).__setitem__(key, value)
print "Succesfully inserted!"
else:
print "Dictionary full!"

Categories