Correct Usage Of A Getter/setter For Dictionary Values
Solution 1:
If you are willing to use syntax like obj['happy'] = 14
then you could use __getitem__
and __setitem__
:
def__getitem__(self, key):
if key notin self.traits.keys():
raise KeyError
...
return traits[key]
def__setitem__(self, key, value):
if key notin 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
classTraitsDict(collections.MutableMapping,dict):
def__getitem__(self,key):
returndict.__getitem__(self,key)
def__setitem__(self, key, value):
value = int(value)
ifnot1 <= 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):
returndict.__iter__(self)
def__len__(self):
returndict.__len__(self)
def__contains__(self, x):
returndict.__contains__(self,x)
classPerson(object):
def__init__(self):
self.traits=TraitsDict({'happy': 0, 'worker': 0, 'honest': 0})
p=Person()
print(p.traits['happy'])
# 0
p.traits['happy']=1print(p.traits['happy'])
# 1
p.traits['happy']=14# ValueError: 14 not in range [1,10]
Solution 2:
Some obvious tips come to my mind first:
- Do not use
.keys()
method when checking for existence of some key (instead ofif key not in self.traits.keys()
useif key not in self.traits
). - Do not explicitly throw KeyError exception - it is thrown if you try to access inexistent key.
Your code could look like this after above changes:
defgetTrait(self, key):
return traits[key]
defsetTrait(self, key, value):
if key notin self.traits:
raise KeyError
value = int(value)
if value < 1or value > 10:
raise ValueError
traits[key] = value
Ps. I did no check the correctness of your code thoroughly - there may be some other issues.
Solution 3:
and new traits should not be allowed to be added.
The natural way to do this is to use an object instead of a dictionary, and set the class' __slots__
.
The value for each trait should be an int in the range 1-10... I want getter/setters so I can make sure these constraints are being kept.
The natural way to do this is to use an object instead of a dictionary, so that you can write getter/setter logic that's part of the class, and wrap them up as properties. Since all these properties will work the same way, we can do some refactoring to write code that generates a property given an attribute name.
The following is probably over-engineered:
defone_to_ten(attr):
defget(obj): returngetattr(obj, attr)
defset(obj, val):
val = int(val)
ifnot1 <= val <= 10: raise ValueError
setattr(obj, attr, val)
returnproperty(get, set)
defcreate_traits_class(*traits):
classTraits(object):
__slots__ = ['_' + trait for trait in traits]
for trait in traits: locals()[trait] = one_to_ten('_' + trait)
def__init__(self, **kwargs):
for k, v in kwargs.items(): setattr(self, k, v)
for trait in traits: asserthasattr(self, trait), "Missing trait in init"def__repr__(self):
return'Traits(%s)' % ', '.join(
'%s = %s' % (trait, getattr(self, trait)) for trait in traits
)
return Traits
example_type = create_traits_class('happy', 'worker', 'honest')
example_instance = example_type(happy=3, worker=8, honest=4)
# and you can set the .traits of some other object to example_instance.
Post a Comment for "Correct Usage Of A Getter/setter For Dictionary Values"