[Python-ideas] Revisiting Immutable Mappings
steve at pearwood.info
Thu Oct 11 07:41:40 EDT 2018
On Thu, Oct 11, 2018 at 02:18:52AM -0700, Zaur Shibzukhov wrote:
> May be the following simple prototype of frozendict could be useful?
> def frozen_error():
> return RuntimeError("frozendict is not mutable")
> class frozendict(dict):
This violates the Liskov Substitution Principle.
Why is that bad? Let's say you have a function that requires a dict,
and following the principle of "Fail Fast", you check the input up
adict: an instance of dict
if isinstance(adict, dict):
# lots of code here
# and deep inside the function, we end up calling:
adict[key] = "something"
I hear people objecting that isinstance checks are "unPythonic".
Perhaps; however, the basic principle applies regardless of whether we
are doing LBYL isinstance checks, test for the presence of a __setitem__
method, or do no up-front tests at all and rely on duck-typing and EAFP.
Our spam() function now accepts dicts, and rejects non-dicts, and works
well, until somebody passes a frozendict:
This passes the isinstance test (or the check for __setitem__). It
passes the documented interface: frozendicts certainly are dicts, since
they are a subclass of dict. But nevertheless, the call to setitem
fails, and the function breaks.
We can get a good idea of how this ought to work by looking at set and
frozenset. frozenset is *not* a subclass of set; it is a distinct class,
so that frozensets are not sets. Rather than having mutator methods
which raise an exception, they simply don't provide those methods at
py> f = frozenset()
py> isinstance(f, set)
py> hasattr(f, 'add')
That's how we should design frozendict.
More information about the Python-ideas