[Python-ideas] Revisiting Immutable Mappings
Steven D'Aprano
steve at pearwood.info
Thu Oct 11 18:16:00 EDT 2018
On Fri, Oct 12, 2018 at 02:45:30AM +1100, Chris Angelico wrote:
> On Fri, Oct 12, 2018 at 2:41 AM Chris Barker - NOAA Federal via
> Python-ideas <python-ideas at python.org> wrote:
> >
> > > This violates the Liskov Substitution Principle.
> >
> > If we REALLY had a time machine, then dict would subclass frozendict,
> > and we’d be all set.
>
> Thanks to virtual subclassing, we can still do this. The question is, should we?
>
> Intuition tells me that a frozen dictionary is a form of dictionary
> that adds restrictions, not that a dictionary is a frozen dictionary
> that you left out to thaw.
No offence Chris, but that's why we shouldn't program by intuition :-)
We don't build Smart cars by starting with a Hummer and slicing off the
bits that aren't needed.
We already have prior art demonstrating best practice in the form
of the Mapping and MutableMapping ABCs, and frozenset versus set for
concrete classes.
Best practice is not to subclass a type and restrict existing
functionality, but to start with a restricted class and extend
functionality. Or not to subclass at all.
Subclassing is not the only way to DRY -- there are other ways for
frozendict and dict to share code other than subclassing, although
they're not necessarily efficient or easy if frozendict is written in
pure Python. One way is delegation to a hidden dict (like the
MappingProxyType, except we ensure the dict is private and not shared).
# Just a sketch, not a full implementation.
class frozendict(object):
def __new__(cls, *args, **kwargs):
instance = super().__new__(cls)
instance.__d = dict(*args, **kwargs)
return instance
# Define only the methods we want, delegating to the
# hidden dict.
def __getitem__(self, key):
return self.__d[key]
def keys(self):
return self.__d.keys()
def items(self):
return self.__d.items()
The trickiest part is probably getting hashing right. I think this would
work:
def __hash__(self):
return hash(tuple(self.items()))
> But as we see from [frozen]set, it's
> probably best to treat them as completely independent classes, both
> implementing the basic Mapping interface.
Indeed.
--
Steve
More information about the Python-ideas
mailing list