创建对象时如何设置字段值?

时间:2018-10-03 11:26:07

标签: python python-3.x python-3.6

我想将idphrase的串联形式初始化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()}'

因此,每次我指定phrasetype时,我都希望自动拥有一个id

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

做到这一点的最佳方法是什么?也许有一些使用https://github.com/ericvsmith/dataclasses来做到这一点的方法?

2 个答案:

答案 0 :(得分:2)

这是特殊方法__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版本中中断,或者被本机实现所取代...

答案 1 :(得分:1)

您可以在__ 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'