Reputation:
I have this subclass of a named tuple type:
class User(namedtuple('User', ['first_name'])):
__slots__ = ()
def __new__(cls, *args, **kwargs):
result = super().__new__(cls, *args, **kwargs)
if not result.first_name:
raise InvalidUserError({InvalidUserError.EMPTY_FIRST_NAME})
return result
Creating a new user works as expected:
>>> try: User(first_name='')
... except Exception as e: print(type(e))
<class 'InvalidUserError'>
However, when _replace
is used the __new__
method is not invoked:
>>> User(first_name='foo')._replace(first_name='')
User(first_name='')
Is there a way to guarantee invariants with namedtuple
? I'm using Python 3.4.
Upvotes: 2
Views: 241
Reputation: 601559
Python in general relies on conventions and good documentation rather than on strongly enforcing invariants. Even without _replace()
, you could circumvent User.__new__()
:
>>> class X(tuple): __slots__ = ()
>>> x = C(('',))
>>> x.__class__ = User
>>> x
User(first_name='')
So no, you will never be able to stricly enforce this. Simply avoid using _replace()
, or override it with a version that does call User.__new__()
, or check the invariants at a different level.
An example implementation of _replace()
:
def _replace(self, **kwargs):
return type(self)(**dict(vars(self), **kwargs))
Upvotes: 2