2014-09-09 5 views
1

У меня есть этот подкласс с именем типа кортежа:_replace не вызывает __new__ именованного кортежа подкласс

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 

Создание нового пользователя работает, как ожидалось:

>>> try: User(first_name='') 
... except Exception as e: print(type(e)) 
<class 'InvalidUserError'> 

Однако, когда _replace является Использовался метод __new__ не вызывается:

>>> User(first_name='foo')._replace(first_name='') 
User(first_name='') 

есть ли способ, чтобы гарантировать invarian ts с namedtuple? Я использую Python 3.4.

ответ

2

Python в целом полагается на соглашения и хорошую документацию, а не на сильные принудительные инварианты. Даже без _replace(), вы можете обойти User.__new__():

>>> class X(tuple): __slots__ =() 
>>> x = C(('',)) 
>>> x.__class__ = User 
>>> x 
User(first_name='') 

Так нет, вы никогда не будете иметь возможность stricly обеспечения этого. Просто избегайте использования _replace() или переопределите его версией, вызывающей User.__new__(), или проверьте инварианты на другом уровне.

Пример реализации _replace():

def _replace(self, **kwargs): 
    return type(self)(**dict(vars(self), **kwargs)) 
+0

Моя точка не препятствует уродливых хаков, как ваш пример, а поддерживающий код, я бы на самом деле писать, что бы, безусловно, использовать 'User (...)' и 'user._replace (...)'. – 2014-09-09 10:43:34

+0

@PolymorphicPotato: В этом случае вам придется переопределить '_replace()' своей собственной версией, например. 'Пользователь (** dict (vars (some_user), first_name = 'Joe'))'. –

+0

Это должно сработать. Я мог бы даже создать некоторый базовый класс или декоратор, чтобы сделать это для меня! – 2014-09-09 16:22:08

 Смежные вопросы

  • Нет связанных вопросов^_^