class_docstr: Wrap multiple batchify functions together and apply it to merge inputs from a namedtuple.
  The generated batch samples are stored as a namedtuple with the same structure.
  Each data sample should be a namedtuple. The `i`th batchify
  function stored in `NamedTuple` will be applied on the `i`th attribute of the namedtuple data.
  For example, each data sample is Sample(data=nd_data, label=nd_label).
  You can wrap two batchify functions using
  `NamedTuple(Sample, {'data': DataBatchify, 'label': LabelBatchify})` to
  batchify nd_data and nd_label correspondingly. The result will be stored as a Sample object
  and you can access the data and label via `sample.data` and `sample.label`, correspondingly.
  Parameters
  ----------
  container
      The object that constructs the namedtuple.
  fn_info
      The information of the inner batchify functions.
  Examples
  --------
  >>> from codel.data.batchify import NamedTuple, Pad, Stack
  >>> from collections import namedtuple
  >>> SampleData = namedtuple('SampleData', ['data', 'label'])
  >>> a = SampleData([1, 2, 3, 4], 0)
  >>> b = SampleData([5, 7], 1)
  >>> c = SampleData([1, 2, 3, 4, 5, 6, 7], 0)
  >>> batchify_fn = NamedTuple(SampleData, {'data': Pad(), 'label': Stack()})
  >>> sample = batchify_fn([a, b, c])
  >>> sample
  SampleData(data=
  [[1. 2. 3. 4. 0. 0. 0.]
   [5. 7. 0. 0. 0. 0. 0.]
   [1. 2. 3. 4. 5. 6. 7.]]
  <NDArray 3x7 @cpu_shared(0)>, label=
  [0 1 0]
  <NDArray 3 @cpu_shared(0)>)
  >>> sample.data
  <BLANKLINE>
  [[1. 2. 3. 4. 0. 0. 0.]
   [5. 7. 0. 0. 0. 0. 0.]
   [1. 2. 3. 4. 5. 6. 7.]]
  <NDArray 3x7 @cpu_shared(0)>
  >>> # Let's consider to use a list
  >>> batchify_fn = NamedTuple(SampleData, [Pad(), Stack()])
  >>> batchify_fn([a, b, c])
  SampleData(data=
  [[1. 2. 3. 4. 0. 0. 0.]
   [5. 7. 0. 0. 0. 0. 0.]
   [1. 2. 3. 4. 5. 6. 7.]]
  <NDArray 3x7 @cpu_shared(0)>, label=
  [0 1 0]
  <NDArray 3 @cpu_shared(0)>)
docstr: Batchify the input data.
  Parameters
  ----------
  data
      The samples to batchfy. Each sample should be a namedtuple.
  Returns
  -------
  ret
      A namedtuple of length N. Contains the batchified result of each attribute in the input.
docstr_tok: ["Batchify","the","input","data",".","Parameters","--","--","--","--","--","data","The","samples","to","batchfy",".","Each","sample","should","be","a","namedtuple",".","Returns","--","--","--","-","ret","A","namedtuple","of","length","N.","Contains","the","batchified","result","of","each","attribute","in","the","input","."]
code: def __call__(self, data: t_List[t_NamedTuple]) -> t_NamedTuple:
      """Batchify the input data.
      Parameters
      ----------
      data
          The samples to batchfy. Each sample should be a namedtuple.
      Returns
      -------
      ret
          A namedtuple of length N. Contains the batchified result of each attribute in the input.
      """
      if not isinstance(data[0], self._container):
          raise ValueError(
              "The samples should have the same type as the stored namedtuple."
              " data[0]={}, container={}".format(
                  data[0], self._container
              )
          )
      ret = []
      for i, ele_fn in enumerate(self._fn_l):
          ret.append(ele_fn([ele[i] for ele in data]))
      return self._container(*ret)
  
imports: import math
  from typing import AnyStr
  from typing import Callable
  from typing import Dict
  from typing import List
  from typing import NamedTuple
  from typing import Tuple
  from typing import Union
  import numpy
  import torch
  from ..utils import to_torch_dtype
label_desc: Use image node_id 2 for calling the NamedTuple obj's underlying member method code with example usage: obj.__call__(data) and returns: self