I'm pretty new to Python, so if there's anything here that's flat-out bad, please point it out.
I have an object with this dictionary:
traits = {'happy': 0, 'worker': 0, 'honest': 0}
The value for each trait should be an int in the range 1-10, and new traits should not be allowed to be added. I want getter/setters so I can make sure these constraints are being kept. Here's how I made the getter and setter now:
def getTrait(self, key):
if key not in self.traits.keys():
raise KeyError
return traits[key]
def setTrait(self, key, value):
if key not in self.traits.keys():
raise KeyError
value = int(value)
if value < 1 or value > 10:
raise ValueError
traits[key] = value
I read on this website about the property()
method. But I don't see an easy way to make use of it for getting/setting the values inside the dictionary. Is there a better way to do this? Ideally I would like the usage of this object to be obj.traits['happy'] = 14
, which would invoke my setter method and throw a ValueError since 14 is over 10.
Getters and Setters in python are often used when: We use getters & setters to add validation logic around getting and setting a value. To avoid direct access of a class field i.e. private variables cannot be accessed directly or modified by external user.
Getters and setters are used to protect your data, particularly when creating classes. For each instance variable, a getter method returns its value while a setter method sets or updates its value. Given this, getters and setters are also known as accessors and mutators, respectively.
The well-known, or I should say the traditional way to access a value in a dictionary is by referring to its key name, inside a square bracket. Notice that when you want to access the value of the key that doesn't exist in the dictionary will result in a KeyError.
Using getters and setters, is always, in my opinion good practice. One thing you should avoid is to have external entities mess with the internal structure of your class at will. Typical example, consider having a dateOfBirth parameter.
If you are willing to use syntax like obj['happy'] = 14
then you could use __getitem__
and __setitem__
:
def __getitem__(self, key):
if key not in self.traits.keys():
raise KeyError
...
return traits[key]
def __setitem__(self, key, value):
if key not in self.traits.keys():
raise KeyError
...
traits[key] = value
If you really do want obj.traits['happy'] = 14
then you could define a subclass of dict
and make obj.traits
an instance of this subclass.
The subclass would then override __getitem__
and __setitem__
(see below).
PS. To subclass dict
, inherit from both collections.MutableMapping
, and dict
. Otherwise, dict.update
would not call the new __setitem__
.
import collections
class TraitsDict(collections.MutableMapping,dict):
def __getitem__(self,key):
return dict.__getitem__(self,key)
def __setitem__(self, key, value):
value = int(value)
if not 1 <= value <= 10:
raise ValueError('{v} not in range [1,10]'.format(v=value))
dict.__setitem__(self,key,value)
def __delitem__(self, key):
dict.__delitem__(self,key)
def __iter__(self):
return dict.__iter__(self)
def __len__(self):
return dict.__len__(self)
def __contains__(self, x):
return dict.__contains__(self,x)
class Person(object):
def __init__(self):
self.traits=TraitsDict({'happy': 0, 'worker': 0, 'honest': 0})
p=Person()
print(p.traits['happy'])
# 0
p.traits['happy']=1
print(p.traits['happy'])
# 1
p.traits['happy']=14
# ValueError: 14 not in range [1,10]
Some obvious tips come to my mind first:
.keys()
method when checking for existence of some key (instead of if key not in self.traits.keys()
use if key not in self.traits
).Your code could look like this after above changes:
def getTrait(self, key):
return traits[key]
def setTrait(self, key, value):
if key not in self.traits:
raise KeyError
value = int(value)
if value < 1 or value > 10:
raise ValueError
traits[key] = value
Ps. I did no check the correctness of your code thoroughly - there may be some other issues.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With