I\'d like to create my own type of build-in namedtuple that has some extra features. Let\'s say we create a class:
from collections import namedtuple
MyClass
namedtuple() uses a string template to generate a class object.
You could use that same technique for your modified version; but do use the code already generated for you as a base class:
import sys
from collections import OrderedDict
_typechecking_class_template = """\
from collections import namedtuple as _namedtuple
class {typename}(_namedtuple({typename!r}, {field_names!r})):
'{typename}({arg_list})'
__slots__ = ()
def __new__(_cls, {arg_list}):
'Create new instance of {typename}({arg_list})'
for name, type_ in _cls._field_types.items():
value = locals()[name]
if not isinstance(value, type_):
raise TypeError("Incorrect type {{!r}} for {{}}, expected {{!r}}".format(
type(value).__name__, name, type_.__name__))
return tuple.__new__(_cls, ({arg_list}))
"""
def typechecking_namedtuple(typename, field_names, field_types):
if isinstance(field_names, str):
field_names = field_names.replace(',', ' ').split()
field_names = list(map(str, field_names))
typename = str(typename)
class_definition = _typechecking_class_template.format(
typename = typename,
field_names = tuple(field_names),
arg_list = repr(tuple(field_names)).replace("'", "")[1:-1],
)
namespace = dict(__name__='typechecking_namedtuple_%s' % typename)
exec(class_definition, namespace)
result = namespace[typename]
result._field_types = OrderedDict(zip(field_names, field_types))
try:
module = sys._getframe(1).f_globals.get('__name__', '__main__')
result.__module__ = module
except (AttributeError, ValueError):
pass
return result
This lets you produce new type-checking namedtuple classes:
>>> MyClass = typechecking_namedtuple('MyClass', 'field1 field2', (int, float))
>>> MyClass(42, 81.2)
MyClass(field1=42, field2=81.2)
>>> MyClass('fourtytwo', 81.2)
Traceback (most recent call last):
File "", line 1, in
File "", line 16, in __new__
TypeError: Incorrect type 'str' for field1, expected 'int'
>>> MyClass(42, None)
Traceback (most recent call last):
File "", line 1, in
File "", line 16, in __new__
TypeError: Incorrect type 'NoneType' for field2, expected 'float'