numpy_type(type) yields a tuple of valid numpy types that can represent the type specified in
      the given type argument. The type argument may be a numpy type such as numpy.signedinteger, in
      which case a tuple containing only it is returned. Alternately, it may be a string that labels
      a numpy type or a builtin type that should be translated to a numpy type (see below).

    Note that numpy_types() does note intend to reproduce the numpy type hierarchy! If you want to
    perform comparisons on the numpy hierarchy, use numpy's functions. Rather, numpy_type()
    represents types in a mathematical hierarchy, so, for example, numpy_type('real') yields the
    tuple (numpy.floating, numpy.integer, numpy.bool_) because all of these are valid real numbers.

    Valid numpy type strings include:
      * 'bool'
      * 'int' / 'integer' (integers and booleans)
      * 'float' / 'real' (integers, booleans, or floating-point)
      * 'complex' / 'number' (integers, booleans, reals, or complex numbers)
      * 'string' ('unicode' in Python 3, 'bytes' in Python 2)
      * 'unicode'
      * 'bytes'
      * 'chars' (numpy.character)
      * 'object'
      * 'any'
    Valid builtin types include:
      * int (also long in Python 2), like 'int'
      * float, like 'float'
      * complex, like 'complex'
      * str, like 'string'
      * bytes, like 'bytes',
      * unicode, like 'unicode'
      * object, like 'object'
      * None, like 'any'

def numpy_type(type_id):
    '''
    numpy_type(type) yields a tuple of valid numpy types that can represent the type specified in
      the given type argument. The type argument may be a numpy type such as numpy.signedinteger, in
      which case a tuple containing only it is returned. Alternately, it may be a string that labels
      a numpy type or a builtin type that should be translated to a numpy type (see below).

    Note that numpy_types() does note intend to reproduce the numpy type hierarchy! If you want to
    perform comparisons on the numpy hierarchy, use numpy's functions. Rather, numpy_type()
    represents types in a mathematical hierarchy, so, for example, numpy_type('real') yields the
    tuple (numpy.floating, numpy.integer, numpy.bool_) because all of these are valid real numbers.

    Valid numpy type strings include:
      * 'bool'
      * 'int' / 'integer' (integers and booleans)
      * 'float' / 'real' (integers, booleans, or floating-point)
      * 'complex' / 'number' (integers, booleans, reals, or complex numbers)
      * 'string' ('unicode' in Python 3, 'bytes' in Python 2)
      * 'unicode'
      * 'bytes'
      * 'chars' (numpy.character)
      * 'object'
      * 'any'
    Valid builtin types include:
      * int (also long in Python 2), like 'int'
      * float, like 'float'
      * complex, like 'complex'
      * str, like 'string'
      * bytes, like 'bytes',
      * unicode, like 'unicode'
      * object, like 'object'
      * None, like 'any'
    '''
    if is_str(type_id):
        return _numpy_type_names[type_id.lower()]
    elif isinstance(type_id, (list, tuple)):
        return tuple([dt for s in type_id for dt in numpy_type(s)])
    elif type_id is None:
        return _numpy_type_names['any']
    elif type_id in six.integer_types:       return _numpy_type_names['int']
    elif type_id is float:                   return _numpy_type_names['float']
    elif type_id is numbers.Real:            return _numpy_type_names['real']
    elif type_id is complex:                 return _numpy_type_names['complex']
    elif type_id is numbers.Complex:         return _numpy_type_names['complex']
    elif type_id is bool:                    return _numpy_type_names['bool']
    elif type_id is str:                     return _numpy_type_names['string']
    elif type_id is unicode:                 return _numpy_type_names['unicode']
    elif type_id is bytes:                   return _numpy_type_names['bytes']
    elif type_id is object:                  return _numpy_type_names['object']
    elif np.issubdtype(type_id, np.generic): return (type_id,)
    else: raise ValueError('Could not deduce numpy type for %s' % type_id)