<! -->
from collections import Sequence
from inspect import Signature, Parameter
class MutableNamedTuple(Sequence):
"""Abstract Base Class for objects as efficient as mutable
namedtuples.
Subclass and define your named fields with __slots__.
"""
@classmethod
def get_signature(cls):
parameters = [
Parameter(name=slot, kind=Parameter.POSITIONAL_OR_KEYWORD) for slot in cls.__slots__
]
return Signature(parameters=parameters)
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
slots = cls.__slots__
cls.__slots__ = tuple(slots.split()) if isinstance(slots, str) else tuple(slots)
cls.__signature__ = cls.get_signature()
cls.__init__.__signature__ = cls.get_signature()
cls.__doc__ = '{cls.__name__}{cls.__signature__}\n\n{cls.__doc__}'.format(
cls=cls)
def __new__(cls, *args, **kwargs):
if cls is MutableNamedTuple:
raise TypeError("Can't instantiate abstract class MutableNamedTuple")
return super().__new__(cls)
@classmethod
def _get_bound_args(cls, args, kwargs):
return Signature.bind(cls.__signature__, *args, **kwargs).arguments.items()
__slots__ = ()
def __init__(self, *args, **kwargs):
bound_args = self._get_bound_args(args, kwargs)
for slot, value in bound_args:
setattr(self, slot, value)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
from collections import Sequence
from inspect import Signature, Parameter
class MutableNamedTuple(Sequence):
"""Abstract Base Class for objects as efficient as mutable
namedtuples.
Subclass and define your named fields with __slots__.
"""
@classmethod
def get_signature(cls):
parameters = [
Parameter(name=slot, kind=Parameter.POSITIONAL_OR_KEYWORD) for slot in cls.__slots__
]
return Signature(parameters=parameters)
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
slots = cls.__slots__
cls.__slots__ = tuple(slots.split()) if isinstance(slots, str) else tuple(slots)
cls.__signature__ = cls.get_signature()
cls.__init__.__signature__ = cls.get_signature()
cls.__doc__ = '{cls.__name__}{cls.__signature__}\n\n{cls.__doc__}'.format(
cls=cls)
def __new__(cls, *args, **kwargs):
if cls is MutableNamedTuple:
raise TypeError("Can't instantiate abstract class MutableNamedTuple")
return super().__new__(cls)
@classmethod
def _get_bound_args(cls, args, kwargs):
return Signature.bind(cls.__signature__, *args, **kwargs).arguments.items()
__slots__ = ()
def __init__(self, *args, **kwargs):
bound_args = self._get_bound_args(args, kwargs)
for slot, value in bound_args:
setattr(self, slot, value)
def __repr__(self):
return type(self).__name__ + repr(tuple(self))
def __iter__(self):
for name in self.__slots__:
yield getattr(self, name)
def __getitem__(self, index):
return getattr(self, self.__slots__[index])
def __len__(self):
return len(self.__slots__)
>>> MutableNamedTuple()
...
TypeError: Can't instantiate abstract class MutableNamedTuple
>>> help(MNT)
Help on class MNT in module __main__:
class MNT(MutableNamedTuple)
| MNT(foo, bar, baz, quux)
|
| demo mutable named tuple with metasyntactic names
|
...
>>> inspect.getfullargspec(MNT)
>>> FullArgSpec(args=['foo', 'bar', 'baz', 'quux'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
>>> MNT(1, 2)
...
TypeError: missing a required argument: 'baz'
>>> MNT(1, bar=2, baz=2, quux=10, spam='eggs')
...
TypeError: got an unexpected keyword argument 'spam'
>>> m = MNT(1, 2, baz=2, quux=10)
>>> m.foo, m.bar, m.baz, m.quux
(1, 2, 2, 10)
>>> MutableNamedTuple()
...
TypeError: Can't instantiate abstract class MutableNamedTuple
>>> help(MNT)
Help on class MNT in module __main__:
class MNT(MutableNamedTuple)
| MNT(foo, bar, baz, quux)
|
| demo mutable named tuple with metasyntactic names
|
...
>>> inspect.getfullargspec(MNT)
>>> FullArgSpec(args=['foo', 'bar', 'baz', 'quux'], varargs=None, varkw=None, defaults=None, kwonlyargs=[], kwonlydefaults=None, annotations={})
>>> MNT(1, 2)
...
TypeError: missing a required argument: 'baz'
>>> MNT(1, bar=2, baz=2, quux=10, spam='eggs')
...
TypeError: got an unexpected keyword argument 'spam'
>>> m = MNT(1, 2, baz=2, quux=10)
>>> m.foo, m.bar, m.baz, m.quux
(1, 2, 2, 10)