Creating NamedTuples
--------------------
Simple
^^^^^^
The most common way to create a new NamedTuple will be via the functional API::
>>> from aenum import NamedTuple
>>> Book = NamedTuple('Book', 'title author genre', module=__name__)
This creates a ``NamedTuple`` called ``Book`` that will always contain three
items, each of which is also addressable as ``title``, ``author``, or ``genre``.
``Book`` instances can be created using positional or keyword argements or a
mixture of the two::
>>> b1 = Book('Lord of the Rings', 'J.R.R. Tolkien', 'fantasy')
>>> b2 = Book(title='Jhereg', author='Steven Brust', genre='fantasy')
>>> b3 = Book('Empire', 'Orson Scott Card', genre='scifi')
If too few or too many arguments are used a ``TypeError`` will be raised::
>>> b4 = Book('Hidden Empire')
Traceback (most recent call last):
...
TypeError: values not provided for field(s): author, genre
>>> b5 = Book(genre='business')
Traceback (most recent call last):
...
TypeError: values not provided for field(s): title, author
As a ``class`` the above ``Book`` ``NamedTuple`` would look like::
>>> class Book(NamedTuple):
... title = 0
... author = 1
... genre = 2
...
For compatibility with the stdlib ``namedtuple``, NamedTuple also has the
``_asdict``, ``_make``, and ``_replace`` methods, and the ``_fields``
attribute, which all function similarly::
>>> class Point(NamedTuple):
... x = 0, 'horizontal coordinate', 1
... y = 1, 'vertical coordinate', -1
...
>>> class Color(NamedTuple):
... r = 0, 'red component', 11
... g = 1, 'green component', 29
... b = 2, 'blue component', 37
...
>>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__)
>>> pixel = Pixel(99, -101, 255, 128, 0)
>>> pixel._asdict()
OrderedDict([('x', 99), ('y', -101), ('r', 255), ('g', 128), ('b', 0)])
>>> Point._make((4, 5))
Point(x=4, y=5)
>>> purple = Color(127, 0, 127)
>>> mid_gray = purple._replace(g=127)
>>> mid_gray
Color(r=127, g=127, b=127)
>>> pixel._fields
['x', 'y', 'r', 'g', 'b']
>>> Pixel._fields
['x', 'y', 'r', 'g', 'b']
Advanced
^^^^^^^^
The simple method of creating ``NamedTuples`` requires always specifying all
possible arguments when creating instances; failure to do so will raise
exceptions::
>>> class Point(NamedTuple):
... x = 0
... y = 1
...
>>> Point()
Traceback (most recent call last):
...
TypeError: values not provided for field(s): x, y
>>> Point(1)
Traceback (most recent call last):
...
TypeError: values not provided for field(s): y
>>> Point(y=2)
Traceback (most recent call last):
...
TypeError: values not provided for field(s): x
However, it is possible to specify both docstrings and default values when
creating a ``NamedTuple`` using the class method::
>>> class Point(NamedTuple):
... x = 0, 'horizontal coordinate', 0
... y = 1, 'vertical coordinate', 0
...
>>> Point()
Point(x=0, y=0)
>>> Point(1)
Point(x=1, y=0)
>>> Point(y=2)
Point(x=0, y=2)
It is also possible to create ``NamedTuples`` that only have named attributes
for certain fields; any fields without names can still be accessed by index::
>>> class Person(NamedTuple):
... fullname = 2
... phone = 5
...
>>> p = Person('Ethan', 'Furman', 'Ethan Furman',
... 'ethan at stoneleaf dot us',
... 'ethan.furman', '999.555.1212')
>>> p
Person('Ethan', 'Furman', 'Ethan Furman', 'ethan at stoneleaf dot us',
'ethan.furman', '999.555.1212')
>>> p.fullname
'Ethan Furman'
>>> p.phone
'999.555.1212'
>>> p[0]
'Ethan'
In the above example the last named field was also the last field possible; in
those cases where you don't need to have the last possible field named, you can
provide a ``_size_`` of ``TupleSize.minimum`` to declare that more fields are
okay::
>>> from aenum import TupleSize
>>> class Person(NamedTuple):
... _size_ = TupleSize.minimum
... first = 0
... last = 1
...
or, optionally if using Python 3::
>>> class Person(NamedTuple, size=TupleSize.minimum): # doctest: +SKIP
... first = 0
... last = 1
and in use::
>>> Person('Ethan', 'Furman')
Person(first='Ethan', last='Furman')
>>> Person('Ethan', 'Furman', 'ethan.furman')
Person('Ethan', 'Furman', 'ethan.furman')
>>> Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!')
Person('Ethan', 'Furman', 'ethan.furman', 'yay Python!')
>>> Person('Ethan')
Traceback (most recent call last):
...
TypeError: values not provided for field(s): last
Also, for those cases where even named fields may not be present, you can
specify ``TupleSize.variable``::
>>> class Person(NamedTuple):
... _size_ = TupleSize.variable
... first = 0
... last = 1
...
>>> Person('Ethan')
Person('Ethan')
>>> Person(last='Furman')
Traceback (most recent call last):
...
TypeError: values not provided for field(s): first
Creating new ``NamedTuples`` from existing ``NamedTuples`` is simple::
>>> Point = NamedTuple('Point', 'x y')
>>> Color = NamedTuple('Color', 'r g b')
>>> Pixel = NamedTuple('Pixel', Point+Color, module=__name__)
>>> Pixel
<NamedTuple 'Pixel'>
The existing fields in the bases classes are renumbered to fit the new class,
but keep their doc strings and default values. If you use standard
subclassing::
>>> Point = NamedTuple('Point', 'x y')
>>> class Pixel(Point):
... r = 2, 'red component', 11
... g = 3, 'green component', 29
... b = 4, 'blue component', 37
...
>>> Pixel.__fields__
['x', 'y', 'r', 'g', 'b']
You must manage the numbering yourself.