Это проблема, которую должен решать специальный метод __new__
.К сожалению, NamedTuple
предотвращает его перезапись внутри определения.
Но вы можете перезаписать его вне определения с помощью декоратора:
def set_default(attr, func):
def set_new(typ):
old = typ.__new__
sig = inspect.signature(old)
def _new(cls, *args, **kwargs):
bound = sig.bind_partial(cls, *args, **kwargs).arguments
if not attr in bound:
bound[attr] = func(*args, **kwargs)
# print(bound) # uncomment for debug traces
return old(**bound)
typ.__new__ = _new
return typ
return set_new
@set_default('id', lambda phrase, typ: f'{phrase}_{typ.name.lower()}')
class WordItem(NamedTuple):
phrase: str
type: WORD_TYPE
id: str = None
Затем вы можете использовать WordItem:
>>> w = WordItem('a', WORD_TYPE.APPROVED)
>>> w
WordItem(phrase='a', type=<WORD_TYPE.APPROVED: 'approved'>, id='a_approved')
>>> w2 = WordItem('b', WORD_TYPE.APPROVED, 'c')
>>> w2
WordItem(phrase='b', type=<WORD_TYPE.APPROVED: 'approved'>, id='c')
Но :
- NamedTuple предотвращает перезапись
__new__
по причине - , которая пытается реализовать функцию, которая явно запрещена
Если можете, избегайте этого трюка.Он отлично работает в моей версии 3.6, но он может сломаться в будущей версии NamedTuple или быть заменен собственной реализацией ...