Named tuple and default values for optional keyword arguments
Python 3.7
Use the defaults parameter.
>>> from collections import namedtuple
>>> fields = ('val', 'left', 'right')
>>> Node = namedtuple('Node', fields, defaults=(None,) * len(fields))
>>> Node()
Node(val=None, left=None, right=None)
Or better yet, use the new dataclasses library, which is much nicer than namedtuple.
>>> from dataclasses import dataclass
>>> from typing import Any
>>> @dataclass
... class Node:
... val: Any = None
... left: 'Node' = None
... right: 'Node' = None
>>> Node()
Node(val=None, left=None, right=None)
Before Python 3.7
Set Node.__new__.__defaults__
to the default values.
>>> from collections import namedtuple
>>> Node = namedtuple('Node', 'val left right')
>>> Node.__new__.__defaults__ = (None,) * len(Node._fields)
>>> Node()
Node(val=None, left=None, right=None)
Before Python 2.6
Set Node.__new__.func_defaults
to the default values.
>>> from collections import namedtuple
>>> Node = namedtuple('Node', 'val left right')
>>> Node.__new__.func_defaults = (None,) * len(Node._fields)
>>> Node()
Node(val=None, left=None, right=None)
Order
In all versions of Python, if you set fewer default values than exist in the namedtuple, the defaults are applied to the rightmost parameters. This allows you to keep some arguments as required arguments.
>>> Node.__new__.__defaults__ = (1,2)
>>> Node()
Traceback (most recent call last):
...
TypeError: __new__() missing 1 required positional argument: 'val'
>>> Node(3)
Node(val=3, left=1, right=2)
Wrapper for Python 2.6 to 3.6
Here's a wrapper for you, which even lets you (optionally) set the default values to something other than None
. This does not support required arguments.
import collections
def namedtuple_with_defaults(typename, field_names, default_values=()):
T = collections.namedtuple(typename, field_names)
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
Example:
>>> 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)
I subclassed namedtuple and overrode the __new__
method:
from collections import namedtuple
class Node(namedtuple('Node', ['value', 'left', 'right'])):
__slots__ = ()
def __new__(cls, value, left=None, right=None):
return super(Node, cls).__new__(cls, value, left, right)
This preserves an intuitive type hierarchy, which the creation of a factory function disguised as a class does not.
Wrap it in a function.
NodeT = namedtuple('Node', 'val left right')
def Node(val, left=None, right=None):
return NodeT(val, left, right)