Skip to main content
added 56 characters in body
Source Link
Vogel612
  • 25.5k
  • 7
  • 59
  • 141

<! -->

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)

<! -->

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)
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)
deleted 4 characters in body
Source Link
  • Currently you're simply iterating over __slots__ everywhere, but this doesn't handle the case when __slots__ is a string parameter like 'bar'. i.e this shouldn't result in three slots 'b', 'a' and 'r'.
  • Your abstract base class MutableNamedTuple is directly instantiable.
  • The __init__ method only works with positional arguments and has the caller has no idea about its signature and will have to peak into its definition all the time.
  • There's no verification of the arguments passed, hence I can pass more or less number of items to __init__ and it wouldn't complain(in case of less items we will get AttributeError later on).
  • Currently you're simply iterating over __slots__ everywhere, but this doesn't handle the case when __slots__ is a string parameter like 'bar'. i.e this shouldn't result in three slots 'b', 'a' and 'r'.
  • Your abstract base class MutableNamedTuple is directly instantiable.
  • The __init__ method only works with positional arguments and has the caller has no idea about its signature and will have to peak into its definition all the time.
  • There's no verification of the arguments passed, hence I can pass more or less number of items to __init__ and it wouldn't complain(in case of less items we will get AttributeError later on).
  • Currently you're simply iterating over __slots__ everywhere, but this doesn't handle the case when __slots__ is a string parameter like 'bar'. i.e this shouldn't result in three slots 'b', 'a' and 'r'.
  • Your abstract base class MutableNamedTuple is directly instantiable.
  • The __init__ method only works with positional arguments and the caller has no idea about its signature and will have to peak into its definition all the time.
  • There's no verification of the arguments passed, hence I can pass more or less number of items to __init__ and it wouldn't complain(in case of less items we will get AttributeError later on).
Source Link

Here are some issues I noticed in your implementation.

  • Currently you're simply iterating over __slots__ everywhere, but this doesn't handle the case when __slots__ is a string parameter like 'bar'. i.e this shouldn't result in three slots 'b', 'a' and 'r'.
  • Your abstract base class MutableNamedTuple is directly instantiable.
  • The __init__ method only works with positional arguments and has the caller has no idea about its signature and will have to peak into its definition all the time.
  • There's no verification of the arguments passed, hence I can pass more or less number of items to __init__ and it wouldn't complain(in case of less items we will get AttributeError later on).

The following modifications try to address the above issues using meta-programming and other features of Python 3:

<! -->

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__)

Demo:

>>> 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)