I'd like to impose restrictions on a value in a dictionary.
For example, assume that I have the following dictionary
speed = {
'x': 8,
'y': 0,
'z': 4}
And I want the value corresponding to x-key in the speed dictionary always to be less than 10.
For example, if I use it in some funtion
calcuate_speed(speed)
or
if I implement some mathematical transformation
speed["x"] += 1
the value shouldn't be larger than a certain limit I defined.
What is the simplest way to handle it?
You can do it in a next way:
from collections import UserDict
class MyDict(UserDict):
def __setitem__(self, key, value):
if value > 10: # any validation
raise ValueError('Too big!')
super().__setitem__(key, value)
But it will be better to use a class with property.
Full version:
from collections import UserDict
from math import sqrt
class VectorDict(UserDict):
def __init__(self, *args, speed_limit, **kwargs):
self.speed_limit = speed_limit
super().__init__(*args, **kwargs)
def __setitem__(self, key, value):
if self._calc_speed(key, value) > self.speed_limit:
raise ValueError('Too big!')
super().__setitem__(key, value)
def _calc_speed(self, replace_key=None, replace_value=0):
square_sum = 0
for key, value in self.items():
if key == replace_key:
value = replace_value
square_sum += value ** 2
return sqrt(square_sum)
#property
def speed(self):
return self._calc_speed()
example_1d = VectorDict(x=1, speed_limit=5)
example_2d = VectorDict(x=1, y=5, speed_limit=7)
example_3d = VectorDict(x=1, y=5, z=3, speed_limit=13)
print(example_3d.speed)
example_3d['x'] += 10 # ValueError: Too big!
Just add some value control in your cycle.
For example:
if (speed['x'] >= 10):
speed['x'] = 9
Define a class MyClass with a property speed:
class MyClass:
def __init__(self):
self.speed = 0
def __setattr__(self, k, v):
if k == 'speed' and v > 10:
return
self.__dict__[k] = v
Here I'm just ignoring any changes to speed if it's not a valid value, but you can handle it differently as you per your needs (e.g. raise an exception)
Here's a demo:
m = MyClass()
m.speed = 8
print(m.speed)
m.speed += 1
print(m.speed)
m.speed += 1
print(m.speed)
m.speed += 1
print(m.speed)
We get the following output:
8
9
10
10
Related
I have a class called newInteger, and a variable called num, but I would like num to be a newInteger() instead of an int(). Code below.
class newInteger(int):
def __init__(self, value):
self.value = value
num = 10
I want the line num = 10 to act as if it is num = newInteger(10). Thanks to anyone who can help me with this.
You can run a small thread parallel to your main program that replaces all created integers to newInteger:
import threading
import time
class newInteger(int):
def __init__(self, value):
self.value = value
def __str__(self):
return "newInteger " + str(self.value)
def replace_int():
while True:
g = list(globals().items())
for n, v in g:
if type(v) == int:
globals()[n] = newInteger(v)
threading.Thread(target=replace_int, daemon=True).start()
num = 10
time.sleep(1)
print(num)
But this is unpythonic and will be realy hard to debug. You should just use a explicit conversion like #johnashu proposed
I am not sure if this is what you mean but if youassign the class to a variabl. then it will be an instance of that class..
example:
class newInteger(int):
def __init__(self, value):
self.value = value
num = 10
if num == 10:
num = newInteger(10)
prints:
hello
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)
I have a little problem with two different classes and two methods from the same class. I have a class B which is using both methods from class a which seems to work fine.
The problem however is that the first method from class a (insert) changes a list which the second method (lookup) from this class should use. It is using the global list which is still initiated with only zeroes. So I have no idea how to tell the method to use the HashMap from the insert method :/ I Hope somebody can help, thank you!
""" PUBLIC MEMBERS
Insert the given key (given as a string) with the given value (given as
an integer). If the hash table already contains an entry for the given key,
update the value of this entry with the given value.
"""
class Map:
global m
m = 10000
global HashMap
HashMap = []
for i in range(m):
HashMap.append(0)
#classmethod
def insert(self, key, value):
"""
>>> Map.insert("hi", 9)
[4,53]
"""
self.key = key
self.value = value
asci = 0
for i in key:
asci += ord(i)
hashindex = (asci%m)*2
print(hashindex)
print(HashMap[hashindex])
if HashMap[hashindex] == key:
HashMap[hashindex + 1] = value
else:
while HashMap[hashindex] != 0:
hashindex = ((asci+1)%m)*2
HashMap[hashindex] = key
HashMap[hashindex+1] = value
""" Check if there exists an entry with the given key in the hash table.
If such an entry exists, return its associated integer value.
Otherwise return -1.
"""
#classmethod
def lookup(self, key):
self.key = key
ascilookup = 0
for i in key:
ascilookup += ord(i)
indexlookup = (ascilookup%m)*2
for j in HashMap:
if HashMap[j]==key:
return HashMap[j + 1]
elif HashMap[j]==0:
return "-1"
else:
j =((j+1)%m)*2
if __name__ == "__main__":
import doctest
doctest.testmod()
This is a far simpler implementation of a map in python:
class Map:
HashMap = {}
def __init__(self,leng):
for i in range(leng):
self.HashMap[str(i)]=0
def insert(self, key, value):
self.HashMap[key]=value
def lookup(self, key):
for each in self.HashMap.iterkeys():
if each == key:
return self.HashMap[each]
return None
EDIT without using a dictionary, using two lists is easier:
class Map:
keys = []
values = []
def __init__(self,leng):
for i in range(leng):
self.keys.append(str(i))
self.values.append(0)
#classmethod
def insert(self, key, value):
self.keys.append(key)
self.values.append(value)
#classmethod
def lookup(self, key):
for x in range(0, len(self.keys)):
if self.keys[x] == key:
return self.values[x]
return None
I've a dictionary such as this:
my_dict=collections.OrderedDict([((123, 1), 'qwe'), ((232, 1), 'asd'), ((234, 2), 'zxc'), ((6745, 2), 'aaa'), ((456, 3), 'bbb')])
The combination of the tuple is always unique and I would like to maintain the order of insertion, and hence OrderedDict. I've a well over ~10K items in the dict. How can I efficiently maintain a counter that gives the count of the second element in the tuple? Basically, I need to know the count whenever I would like to add/delete an item in the key. Right now I just iterate through my_dict and get the counter everytime but it seems to be very expensive to do that.
In the above example I want the output to be:
1:2 # As in 1 occurs 2 times
2:2
3:1
Right now I do the following:
from collections import OrderedDict, Counter
my_dict = OrderedDict()
my_dict[(123,1)] = 'qwe'
my_dict[(232,1)] = 'asd'
my_dict[(234,2)] = 'zxc'
my_dict[(6745,2)] = 'aaa'
my_dict[(456,3)] = 'bbb'
cnt = []
for item in my_dict.keys():
cnt.append(item[1])
print Counter(cnt)
I'm not sure if this is the best way but is there a way to override the the = operator and pop function, such that it adds or subtracts a count every time I do that operation?
Getting a Counter to work nicely with an OrderedDict is probably going to require some subclassing. Here's something that might work (I've only implemented __setitem__ and __getitem__, but if you'd like a more robust implementation, let me know):
import collections
class CountedOrderedDict(collections.OrderedDict):
def __init__(self, *args, **kwargs):
self.counter = collections.Counter()
super(CountedOrderedDict, self).__init__(*args, **kwargs)
def __delitem__(self, key):
super(CountedOrderedDict, self).__delitem__(key)
self.counter[key[1]] -= 1
def __setitem__(self, key, value):
if key not in self:
self.counter[key[1]] += 1
super(CountedOrderedDict, self).__setitem__(key, value)
Example usage:
>>> my_dict = CountedOrderedDict({(123,1): 'sda', (232,1) : 'bfd', (234,2) : 'csd', (6745,2) : 'ds', (456,3) : 'rd'})
>>> my_dict.counter
Counter({'1': 2, '2': 2, '3': 1})
>>> del my_dict[(123,1)]
>>> my_dict.counter
Counter({'2': 2, '1': 1, '3': 1})
>>> my_dict[(150,1)] = "asdf"
>>> my_dict.counter
Counter({'1': 2, '2': 2, '3': 1})
Here's a more general CountedOrderedDict implementation that takes a key function as a parameter.
import collections
class CountedOrderedDict(collections.OrderedDict):
def __init__(self, key=lambda k: k, *args, **kwargs):
self.counter = collections.Counter()
self.key_transform = key
super(CountedOrderedDict, self).__init__(*args, **kwargs)
def __delitem__(self, key):
super(CountedOrderedDict, self).__delitem__(key)
self.counter[self.key_transform(key)] -= 1
def __setitem__(self, key, value):
if key not in self:
self.counter[self.key_transform(key)] += 1
super(CountedOrderedDict, self).__setitem__(key, value)
For your needs, you'd instantiate it like so:
my_dict = CountedOrderedDict(key=lambda k: k[1])
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!"