snippetpythonModerate
Create dictionary with default immutable keys
Viewed 0 times
createwithkeysdefaultimmutabledictionary
Problem
I've created a dictionary subclass with a set of default immutable keys.
Here are a few tests to check that it's working correctly:
Is this the proper way to create the subclass?
Background
My search to do this correctly started at the StackOverflow question "How to 'perfectly' override a dict" where I discovered there were two approaches to doing this. One was sub-classing an Abstract Base Class from the
The justification for actually sub-classing
from collections import MutableMapping
class Bones(MutableMapping):
"""Basic dict subclass for bones, that forbids bones from being deleted
and new bones from being added."""
def __init__(self, init_val=0):
self.bones = {"between_hips": init_val,
"bicep_right": init_val,
"bicep_left": init_val,
"arm_right": init_val,
"arm_left": init_val,
"thigh_right": init_val,
"thigh_left": init_val,
"leg_right": init_val,
"leg_left": init_val,
"spine_lower": init_val,
"spine_upper": init_val}
def __getitem__(self, item):
return self.bones[item]
def __setitem__(self, key, value):
if key in self.bones:
self.bones[key] = value
else:
raise KeyError("Can't add a new bone!")
def __delitem__(self, key):
raise TypeError("Can't delete bones")
def __iter__(self):
return iter(self.bones)
def __len__(self):
return len(self.bones)Here are a few tests to check that it's working correctly:
from bones import Bones
bn = Bones()
bn["between_hips"] = 1
# these should all raise errors
bn["foo"] = 1
del bn["between_hips"]Is this the proper way to create the subclass?
Background
My search to do this correctly started at the StackOverflow question "How to 'perfectly' override a dict" where I discovered there were two approaches to doing this. One was sub-classing an Abstract Base Class from the
collections module and the other was actually sub-classing dict.The justification for actually sub-classing
dict confused me. Specifically the discussion of the object properties of __dict__ and __slots__. As best as I can tell from this question, I should be using `__sSolution
When reading the title of the question, I wondered: "Why use a dict subclass when a regular class with
Slots defines attributes names that are reserved for the use as attributes for the instances of the class. However, no more attributes can be added to the instances when defining
However,
You can, however, still define them in
Now the only thing left is to add a dictionary-like interface (and/or inherit from
Or not necessarily, since you can still access the
Note that with this implementation, you can still delete values, which you try to prohibit in your original implementation:
If you wish to prevent that, you need to define
__slots__ can do?". And judging by the interface, it seems that it may fit.Slots defines attributes names that are reserved for the use as attributes for the instances of the class. However, no more attributes can be added to the instances when defining
__slots__ on the class:>>> class Example:
... __slots__ = ('a', 'b', 'c')
...
>>> e = Example()
>>> e.a = 3
>>> e.d = 4
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'Example' object has no attribute 'd'
>>> e.a
3However,
__slots__ are not auto-populated and doing so at class level is prohibited:>>> class Example:
... __slots__ = ('a', 'b', 'c')
... a = 3
... b = 4
... c = 5
...
Traceback (most recent call last):
File "", line 1, in
ValueError: 'a' in __slots__ conflicts with class variableYou can, however, still define them in
__init__:class Bones:
__slots__ = (
'between_hips',
'bicep_right',
'bicep_left',
'arm_right',
'arm_left',
'thigh_right',
'thigh_left',
'leg_right',
'leg_left',
'spine_lower',
'spine_upper',
)
def __init__(self, default_value=0):
for attribute in self.__slots__:
setattr(self, attribute, default_value)Now the only thing left is to add a dictionary-like interface (and/or inherit from
collections.abc.Mapping):def __getitem__(self, key):
return getattr(self, key)
def __setitem__(self, key, value):
setattr(self, key, value)
def __len__(self):
return len(self.__slots__)
def __iter__(self):
return iter(self.__slots__)
def items(self):
for attribute in self.__slots__:
yield attribute, getattr(self, attribute)Or not necessarily, since you can still access the
__slots__ as regular attributes, it all depend on your use case.Note that with this implementation, you can still delete values, which you try to prohibit in your original implementation:
>>> b = Bones()
>>> b.bicep_right
0
>>> b['arm_left'] = 6
>>> b.arm_left
6
>>> del b['leg_left']
Traceback (most recent call last):
File "", line 1, in
AttributeError: __delitem__
>>> del b.leg_left
>>> b.leg_left
Traceback (most recent call last):
File "", line 1, in
AttributeError: leg_leftIf you wish to prevent that, you need to define
__delattr__ on your class as well:def __delattr__(self, key):
raise AttributeError(key)Code Snippets
>>> class Example:
... __slots__ = ('a', 'b', 'c')
...
>>> e = Example()
>>> e.a = 3
>>> e.d = 4
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Example' object has no attribute 'd'
>>> e.a
3>>> class Example:
... __slots__ = ('a', 'b', 'c')
... a = 3
... b = 4
... c = 5
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: 'a' in __slots__ conflicts with class variableclass Bones:
__slots__ = (
'between_hips',
'bicep_right',
'bicep_left',
'arm_right',
'arm_left',
'thigh_right',
'thigh_left',
'leg_right',
'leg_left',
'spine_lower',
'spine_upper',
)
def __init__(self, default_value=0):
for attribute in self.__slots__:
setattr(self, attribute, default_value)def __getitem__(self, key):
return getattr(self, key)
def __setitem__(self, key, value):
setattr(self, key, value)
def __len__(self):
return len(self.__slots__)
def __iter__(self):
return iter(self.__slots__)
def items(self):
for attribute in self.__slots__:
yield attribute, getattr(self, attribute)>>> b = Bones()
>>> b.bicep_right
0
>>> b['arm_left'] = 6
>>> b.arm_left
6
>>> del b['leg_left']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delitem__
>>> del b.leg_left
>>> b.leg_left
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: leg_leftContext
StackExchange Code Review Q#157297, answer score: 12
Revisions (0)
No revisions yet.