
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. https://en.wikipedia.org/wiki/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 front: def spam(adict): """Input: 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: spam(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 all: py> f = frozenset() py> isinstance(f, set) False py> hasattr(f, 'add') False That's how we should design frozendict. -- Steve