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 gluonnlp.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)>)
code: def __init__(
      self,
      container: t_NamedTuple,
      fn_info: t_Union[
          t_List[t_Callable],
          t_Tuple[t_Callable],
          t_Dict[AnyStr, t_Callable],
      ],
  ):
      self._container = container
      if isinstance(fn_info, (list, tuple)):
          if len(container._fields) != len(fn_info):
              raise ValueError(
                  "Attributes mismatch! Required fields={}, fn_info={}".format(
                      container._fields, fn_info
                  )
              )
      elif isinstance(fn_info, dict):
          for name in container._fields:
              if name not in fn_info:
                  raise ValueError(
                      "Attribute {} has not been assigned a callable. "
                      "Required fields={}, Found fields={}".format(
                          name, container._fields, fn_info.keys()
                      )
                  )
          if len(container._fields) != len(fn_info):
              raise ValueError(
                  "Attributes mimatch! Required fields={}, Found fields={}".format(
                      container._fields, fn_info.keys()
                  )
              )
          fn_info = [fn_info[name] for name in container._fields]
      for fn in fn_info:
          if not hasattr(fn, "__call__"):
              raise ValueError(
                  "All batchify functions must be callable."
              )
      self._fn_l = fn_info
  
imports: import warnings
  import math
  from typing import Dict, Callable, NamedTuple, List, Tuple, AnyStr, Union
  import numpy
  import mxnet
  from mxnet.util import is_np_array
label_desc: Use image node_id 1 to create a new NamedTuple object  with example: obj = NamedTuple(container, fn_info)