code: def namedtuple_with_defaults(typename: str, field_names: Union[str, List[str]],
                               default_values: collections.Iterable = ()):
      
      T = collections.namedtuple(typename, field_names)
      # noinspection PyProtectedMember,PyUnresolvedReferences
      T.__new__.__defaults__ = (None,) * len(T._fields)
      if isinstance(default_values, collections.Mapping):
          prototype = T(**default_values)
      else:
          prototype = T(*default_values)
      T.__new__.__defaults__ = tuple(prototype)
      return T
docs: Convenience function for defining a namedtuple with default values
  
      From: https://stackoverflow.com/questions/11351032/namedtuple-and-default-values-for-optional-keyword-arguments
  
      Examples:
          >>> Node = namedtuple_with_defaults('Node', 'val left right')
          >>> Node()
          Node(val=None, left=None, right=None)
          >>> Node = namedtuple_with_defaults('Node', 'val left right', [1, 2, 3])
          >>> Node()
          Node(val=1, left=2, right=3)
          >>> Node = namedtuple_with_defaults('Node', 'val left right', {'right':7})
          >>> Node()
          Node(val=None, left=None, right=7)
          >>> Node(4)
          Node(val=4, left=None, right=7)