Как настроить значение поля при создании объекта? - PullRequest
0 голосов
/ 03 октября 2018

Я хочу инициализировать id как объединение phrase и type

from enum import Enum
from typing import NamedTuple

class WORD_TYPE(str, Enum):
    APPROVED = 'approved'
    FORBIDDEN = 'forbidden'
    RISKY = 'risky'

class WordItem(NamedTuple):
    phrase: str
    type: WORD_TYPE

    id: str = f'{phrase}_{type.name.lower()}'

, поэтому каждый раз, когда я указываю phrase и type, я хочу иметь id автоматически.

word_item = WordItem(phrase='phrase', type=WORD_TYPE.FORBIDDEN)
asssert word_item.id == 'phrase_forbidden'

Каков наилучший способ сделать это?Может быть, есть какой-то способ сделать это, используя https://github.com/ericvsmith/dataclasses?

Ответы [ 2 ]

0 голосов
/ 03 октября 2018

Это проблема, которую должен решать специальный метод __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 или быть заменен собственной реализацией ...

0 голосов
/ 03 октября 2018

Вы можете определить определение внутри метода __ init __.Как то так:

class WordItem(object):
...   def __init__(self, first_name, last_name):
...     self.full_name = f'{first_name}_{last_name}'
...
>>> word_item = WordItem('abc','xyz')
>>> assert word_item.full_name == 'abc_xyz'
...