32. mydict — Extensions to Python’s built-in dict class.

Dict is a dictionary with default values and alternate attribute syntax. CDict is a Dict with lookup cascading into the next level Dict’s if the key is not found in the CDict itself.

The classes have been largely reimplemented in 2019, because many of the reasons for choosing the old implementation are not valid anymore because of improved features of Python. And the migration to Python3 showed the shortcomings of an implementation that was too hackishly meddling with Python internals. Now we have a much cleaner and simpler implementation while still preserving the intended functionality.

(C) 2005,2008,2019 Benedict Verhegghe Distributed under the GNU GPL version 3 or later

32.1. Classes defined in module mydict

class mydict.Dict(default_factory=None, *args, **kargs)[source]

A dictionary with default lookup and attribute and call syntax.

Dict is a dictionary class providing extended functionality over the builtin Python dict class:

  • Items can not only be accessed with dictionary key syntax, but also with attribute and call syntax. Thus, if C is a Dict, an item with key ‘foo’ can be obtained in any of the following equivalent C['foo'], C.foo, C('foo'). The attribute syntax can of course only be used if the key is a string that is valid as Python variable. The first two can also be used to set the value in the Dict: C['foo'] = bar, C.foo = bar. The call syntax does not allow setting a value. See however the Attributes subclass for a dict that does allow C(foo=bar) to set values.

  • A default_factory function can be provided to handle missing keys. The function is then passed the missing key and whatever it returns is returned as the value for that key. Note that this is different from Python’s defaultdict, where the default_factory does not take an argument and can only return a constant value. Without default_factory, the Dict will still produce a KeyError on missing keys.

  • Because the Dict accepts call syntax to lookup keys, another Dict instance can perfectly well be used as default_factory. You can even create a chain of Dict’s to lookup missing keys.

  • An example default_factory is provided which returns None for any key. Using this default_factory will make the Dict accept any key and just return None for the nonexistent keys.

Parameters:
  • default_factory (callable, optional) – If provided, missing keys will be looked up by a call to the default_factory.

  • args (other arguments) – Any other arguments are passed to the dict initialization.

  • kargs (other arguments) – Any other arguments are passed to the dict initialization.

Notes

Watch for this syntax quirks when initializing a Dict:

  • Dict(): an empty Dict without default_factory

  • Dict(callable): an empty Dict with callable as default_factory

  • Dict(noncallable): a Dict without default_factory initialized with data fron noncallable (e.g. a dict or a sequence of tuples)

  • Dict(callable, noncallable): a Dict with callable as default_factory and initialized with data from noncallable.

And since a Dict is callable, but a dict is not, Dict(some_dict) will initialize the data in the Dict, while Dict(some_Dict) will use some_dict as defaults, but stay empty itself.

If a default_factory is provided, all lookup mechanisms (key, attribute, call) will use it for missing keys. The proper way to test whether a key actually exists in the Dict (and not in the default_factory) is using the ‘key in Dict’ operation.

Examples

>>> A = Dict()
>>> A
Dict({})
>>> A['a'] = 0
>>> A.b = 1
>>> # A(c=2) # not allowed
>>> A
Dict({'a': 0, 'b': 1})
>>> sorted(A.keys())
['a', 'b']
>>> 'c' in A
False
>>> A.default_factory is None   # A does have a default_factory attribute
True

Now a Dict with a default_factory.

>>> B = Dict(Dict.returnNone,A)   # The dict is initialized with the values of A
>>> B
Dict({'a': 0, 'b': 1})
>>> sorted(B.keys())
['a', 'b']
>>> 'c' in B
False
>>> B['c'] is None   # lookup nonexistent key returns None
True
>>> B.c is None
True
>>> B('c') is None
True
>>> B.get('c','Surprise?')  # get() does not use default_factory!
'Surprise?'

Using another Dict as default_factory

>>> C = Dict(A,{'b':5, 'c':6})   # Now A is used for missing keys
>>> C
Dict({'b': 5, 'c': 6})
>>> sorted(C.keys())
['b', 'c']
>>> 'a' in C, 'b' in C, 'c' in C        # 'a' is not in C
(False, True, True)
>>> C['a'], C['b'], C['c']         # but still returns a value
(0, 5, 6)
>>> C('a'), C('b'), C('c')         # also with call syntax
(0, 5, 6)
>>> C.a, C.b, C.c                  # or as attributes
(0, 5, 6)
>>> hasattr(C,'a')           # hasattr uses default_factory
True
>>> getattr(C,'a',None)      # getattr too
0
>>> 'a' in C                 # the proper way to test if key exists in C
False
>>> C['b'], C.__missing__('b')  # masked entries of A can also be accessed
(5, 1)
static returnNone(*args, **kargs)[source]

Always returns None.

update(data={}, **kargs)[source]

Add a dictionary to the Dict object.

The data can be a dict or Dict type object.

class mydict.CDict(default_factory=<function Dict.returnNone>, *args, **kargs)[source]

A cascading Dict: missing keys are looked up in all dict values.

This is a Dict subclass with an extra lookup mechanism for missing keys: any value in the Dict that implements the Mapping mechanism (such as dict, Dict and their subclasses) will be tested for the missing key, and the first one found will be returned. If there is no Mapping value providing the key, and a default_factory exists, the key will finally be looked up in the default_factory as well.

Parameters:
  • default_factory (callable, optional) – If provided, missing keys will be looked up by a call to the default_factory.

  • args (other arguments) – Any other arguments are passed to the dict initialization.

  • kargs (other arguments) – Any other arguments are passed to the dict initialization.

Notes

If there are multiple Mapping values in the CDict containing the missing key, it is undefined which one will be used to return a value. CDict’s are therefore usually used where the Mapping values have themselves separate sets of keys.

If any of the values in the CDict is also a CDict, the lookup of missing keys will continue in the Mapping values of that CDict. This results in a cascading lookup as long as the Mapping types in the values as CDict (and not Dict or dict). This is another way of creating chained lookup mechanisms (besides using a Dict as default_factory).

Examples

>>> C = CDict(a=0, d={'a':1, 'b':2})
>>> C
CDict({'a': 0, 'd': {'a': 1, 'b': 2}})
>>> C['a']
0
>>> C['d']
{'a': 1, 'b': 2}
>>> C['b']           # looked up in C['d']
2
>>> 'b' in C
False

Now a CDict with a default_factory.

>>> C = CDict(CDict.returnNone,a=0, d={'a':1, 'b':2})
>>> C
CDict({'a': 0, 'd': {'a': 1, 'b': 2}})
>>> 'c' in C, C['c']      # No 'c', but returns None
(False, None)

32.2. Functions defined in module mydict

mydict.formatDict(d, indent=4)[source]

Format a dict in nicely formatted Python source representation.

Each (key,value) pair is formatted on a line of the form:

key = value

If all the keys are strings containing only characters that are allowed in Python variable names, the resulting text is a legal Python script to define the items in the dict. It can be stored on a file and executed.

>>> d = {'y':'s', 'x':'t'}
>>> D = {'a':0, 'd':d, 'b':1}
>>> print(formatDict(D))
{
    'a': 0,
    'd': {'y': 's', 'x': 't'},
    'b': 1,
    }
>>> D = {'a':0, 'd':Dict(d), 'b':1}
>>> print(formatDict(D))
{
    'a': 0,
    'd': {
        'y': 's',
        'x': 't',
    },
    'b': 1,
}
mydict.returnNone(*args, **kargs)

Always returns None.