 d == e == f
      True

   Providing keyword arguments as in the first example only works for
   keys that are valid Python identifiers.  Otherwise, any valid keys
   can be used.

   Dictionaries preserve insertion order.  Note that updating a key
   does not affect the order.  Keys added after deletion are inserted
   at the end.

      >>> d = {"one": 1, "two": 2, "three": 3, "four": 4}
      >>> d
      {'one': 1, 'two': 2, 'three': 3, 'four': 4}
      >>> list(d)
      ['one', 'two', 'three', 'four']
      >>> list(d.values())
      [1, 2, 3, 4]
      >>> d["one"] = 42
      >>> d
      {'one': 42, 'two': 2, 'three': 3, 'four': 4}
      >>> del d["two"]
      >>> d["two"] = None
      >>> d
      {'one': 42, 'three': 3, 'four': 4, 'two': None}

   Changed in version 3.7: Dictionary order is guaranteed to be
   insertion order.  This behavior was an implementation detail of
   CPython from 3.6.

   These are the operations that dictionaries support (and therefore,
   custom mapping types should support too):

   list(d)

      Return a list of all the keys used in the dictionary *d*.

   len(d)

      Return the number of items in the dictionary *d*.

   d[key]

      Return the item of *d* with key *key*.  Raises a "KeyError" if
      *key* is not in the map.

      If a subclass of dict defines a method "__missing__()" and *key*
      is not present, the "d[key]" operation calls that method with
      the key *key* as argument.  The "d[key]" operation then returns
      or raises whatever is returned or raised by the
      "__missing__(key)" call. No other operations or methods invoke
      "__missing__()". If "__missing__()" is not defined, "KeyError"
      is raised. "__missing__()" must be a method; it cannot be an
      instance variable:

         >>> class Counter(dict):
         ...     def __missing__(self, key):
         ...         return 0
         ...
         >>> c = Counter()
         >>> c['red']
         0
         >>> c['red'] += 1
         >>> c['red']
         1

      The example above shows part of the implementation of
      "collections.Counter".  A different "__missing__" method is used
      by "collections.defaultdict".

   d[key] = value

      Set "d[key]" to *value*.

   del d[key]

      Remove "d[key]" from *d*.  Raises a "KeyError" if *key* is not
      in the map.

   key in d

      Return "True" if *d* has a key *key*, else "False".

   key not in d

      Equivalent to "not key in d".

   iter(d)

      Return an iterator over the keys of the dictionary.  This is a
      shortcut for "iter(d.keys())".

   clear()

      Remove all items from the dictionary.

   copy()

      Return a shallow copy of the dictionary.

   classmethod fromkeys(iterable, value=None, /)

      Create a new dictionary with keys from *iterable* and values set
      to *value*.

      "fromkeys()" is a class method that returns a new dictionary.
      *value* defaults to "None".  All of the values refer to just a
      single instance, so it generally doesn’t make sense for *value*
      to be a mutable object such as an empty list.  To get distinct
      values, use a dict comprehension instead.

   get(key, default=None, /)

      Return the value for *key* if *key* is in the dictionary, else
      *default*. If *default* is not given, it defaults to "None", so
      that this method never raises a "KeyError".

   items()

      Return a new view of the dictionary’s items ("(key, value)"
      pairs). See the documentation of view objects.

   keys()

      Return a new view of the dictionary’s keys.  See the
      documentation of view objects.

   pop(key[, default])

      If *key* is in the dictionary, remove it and return its value,
      else return *default*.  If *default* is not given and *key* is
      not in the dictionary, a "KeyError" is raised.

   popitem()

      Remove and return a "(key, value)" pair from the dictionary.
      Pairs are returned in LIFO (last-in, first-out) order.

      "popitem()" is useful to destructively iterate over a
      dictionary, as often used in set algorithms.  If the dictionary
      is empty, calling "popitem()" raises a "KeyError".

      Changed in version 3.7: LIFO order is now guaranteed. In prior
      versions, "popitem()" would return an arbitrary key/value pair.

   reversed(d)

      Return a reverse iterator over the keys of the dictionary. This
      is a shortcut for "reversed(d.keys())".

      Added in version 3.8.

   setdefault(key, default=None, /)

      If *key* is in the dictionary, return its value.  If not, insert
      *key* with a value of *default* and return *default*.  *default*
      defaults to "None".

   update([other])

      Update the dictionary with the key/value pairs from *other*,
      overwriting existing keys.  Return "None".

      "update()" accepts either another object with a "keys()" method
      (in which case "__getitem__()" is called with every key returned
      from the method) or an iterable of key/value pairs (as tuples or
      other iterables of length two). If keyword arguments are
      specified, the dictionary is then updated with those key/value
      pairs: "d.update(red=1, blue=2)".

   values()

      Return a new view of the dictionary’s values.  See the
      documentation of view objects.

      An equality comparison between one "dict.values()" view and
      another will always return "False". This also applies when
      comparing "dict.values()" to itself:

         >>> d = {'a': 1}
         >>> d.values() == d.values()
         False

   d | other

      Create a new dictionary with the merged keys and values of *d*
      and *other*, which must both be dictionaries. The values of
      *other* take priority when *d* and *other* share keys.

      Added in version 3.9.

   d |= other

      Update the dictionary *d* with keys and values from *other*,
      which may be either a *mapping* or an *iterable* of key/value
      pairs. The values of *other* take priority when *d* and *other*
      share keys.

      Added in version 3.9.

   Dictionaries and dictionary views are reversible.

      >>> d = {"one": 1, "two": 2, "three": 3, "four": 4}
      >>> d
      {'one': 1, 'two': 2, 'three': 3, 'four': 4}
      >>> list(reversed(d))
      ['four', 'three', 'two', 'one']
      >>> list(reversed(d.values()))
      [4, 3, 2, 1]
      >>> list(reversed(d.items()))
      [('four', 4), ('three', 3), ('two', 2), ('one', 1)]

   Changed in version 3.8: Dictionaries are now reversible.

See also:

  "types.MappingProxyType" can be used to create a read-only view of a
  "dict".


Dictionary view objects
=======================

The objects returned by "dict.keys()", "dict.values()" and
"dict.items()" are *view objects*.  They provide a dynamic view on the
dictionary’s entries, which means that when the dictionary changes,
the view reflects these changes.

Dictionary views can be iterated over to yield their respective data,
and support membership tests:

len(dictview)

   Return the number of entries in the dictionary.

iter(dictview)

   Return an iterator over the keys, values or items (represented as
   tuples of "(key, value)") in the dictionary.

   Keys and values are iterated over in insertion order. This allows
   the creation of "(value, key)" pairs using "zip()": "pairs =
   zip(d.values(), d.keys())".  Another way to create the same list is
   "pairs = [(v, k) for (k, v) in d.items()]".

   Iterating views while adding or deleting entries in the dictionary
   may raise a "RuntimeError" or fail to iterate over all entries.

   Changed in version 3.7: Dictionary order is guaranteed to be
   insertion order.

x in dictview

   Return "True" if *x* is in the underlying dictionary’s keys, values
   or items (in the latter case, *x* should be a "(key, value)"
   tuple).

reversed(dictview)

   Return a reverse iterator over the keys, values or items of the
   dictionary. The view will be iterated in reverse order of the
   insertion.

   Changed in version 3.8: Dictionary views are now reversible.

dictview.mapping

   Return a "types.MappingProxyType" that wraps the original
   dictionary to which the view refers.

   Added in version 3.10.

Keys views are set-like since their entries are unique and *hashable*.
Items views also have set-like operations since the (key, value) pairs
are unique and the keys are hashable. If all values in an items view
are hashable as well, then the items view can interoperate with other
sets. (Values views are not treated as set-like since the entries are
generally not unique.)  For set-like views, all of the operations
defined for the abstract base class "collections.abc.Set" are
available (for example, "==", "<", or "^").  While using set
operators, set-like views accept any iterable as the other operand,
unlike sets which only accept sets as the input.

An example of dictionary view usage:

   >>> dishes = {'eggs': 2, 'sausage': 1, 'bacon': 1, 'spam': 500}
   >>> keys = dishes.keys()
   >>> values = dishes.values()

   >>> # iteration
   >>> n = 0
   >>> for val in values:
   ...     n += val
   ...
   >>> print(n)
   504

   >>> # keys and values are iterated over in the same order (insertion order)
   >>> list(keys)
   ['eggs', 'sausage', 'bacon', 'spam']
   >>> list(values)
   [2, 1, 1, 500]

   >>> # view objects are dynamic and reflect dict changes
   >>> del dishes['eggs']
   >>> del dishes['sausage']
   >>> list(keys)
   ['bacon', 'spam']

   >>> # set operations
   >>> keys & {'eggs', 'bacon', 'salad'}
   {'bacon'}
   >>> keys ^ {'sausage', 'juice'} == {'juice', 'sausage', 'bacon', 'spam'}
   True
   >>> keys | ['juice', 'juice', 'juice'] == {'bacon', 'spam', 'juice'}
   True

   >>> # get back a read-only proxy for the original dictionary
   >>> values.mapping
   mappingproxy({'bacon': 1, 'spam': 500})
   >>> values.mapping['spam']
   500
a