如何为Python的新NamedTuple类型定义构造函数?

时间:2018-08-21 07:43:03

标签: python oop

您可能知道,这是在python中定义命名元组的最新类型:

.react-datepicker__navigation--next {
    background: url(../images/rightArrow.png) no-repeat;
    width: 15px;
    height: 15px;
    border: none;
}

定义类型后,Python解释器定义一个获取ID和名称的默认构造函数,您可以使用字段实例化一个新对象。 现在,我想使用一个字符串初始化一个新对象,并在函数中对其进行解析。我该如何定义另一个构造函数而不破坏良好的默认构造函数?

2 个答案:

答案 0 :(得分:4)

  

如何定义另一个构造函数而不破坏良好的默认构造函数?

不能。 Python类不能具有多个__new__方法(或者,如果您要表示“ initializer”,__init__方法),则只能是一个。


但是有一个简单的方法可以解决此问题:备用构造函数惯用法:您编写了@classmethod,它提供了另一种构造实例的方法。标准库中有很多示例,例如datetime.nowdatetime.utcfromtimestamp。基本内置类型中甚至有一些示例,例如int.from_bytes

这是它的工作方式:

class MyType(NamedTuple):
    id: int = 0
    name: str = 0

    @classmethod
    def from_string(cls, string_to_parse):
        id, name = … your parsing code here …
        return cls(id, name)

这当然是您对collections.namedtuple子类,@dataclass或具有太多不同构造方法的普通类所做的相同事情。


如果确实要这样做,另一种方法是为丑陋的构造函数提供仅关键字的参数,或者根据传递的内容而具有不同含义的参数。使用NamedTuple,您将不得不以这种方式插入一个额外的类,或者在创建后对该类进行monkeypatch,因为否则,没有记录的方法可以获取默认的构造函数实现。

所以:

class _MyType(NamedTuple):
    id: int = 0
    name: str = 0

class MyType(_MyType):
    def __new__(cls, id: int=None, name: str=None, *, parseything: str=None):
        if parseything:
            if id is not None or str is not None:
                raise TypeError("don't provide both")
            id, name = … your parsing code here …
        return super().__new__(cls, id, name)

…,或者,如果您更喜欢猴子补丁:

class MyType(NamedTuple):
    id: int = 0
    name: str = 0

_new = MyType.__new__
def __new__(cls, id=None, name=None, *, parseything=None):
    if parseything:
        if id is not None or str is not None:
            raise TypeError("don't provide both")
        id, name = … your parsing code here …
    return _new(cls, id, name)
MyType.__new__ = __new__
del _new
del __new__

…,或者,如果您想要更多的range风格的丑陋API,则可以使用以下任一方法进行操作:

def __new__(cls, id_or_parsey_thing: Union[int,str]=None, 
            name: str=None):
    if isinstance(id_or_parsey_thing, str):
        if name is not None:
            raise TypeError("don't provide both")
        id, name = … your parsing code here …
    else:
        id = id_or_parsey_thing
    # super().__new__ or _new here

答案 1 :(得分:0)

是的,自Python 3.6起,namedtuple有一个新的替代方法-NamedTuple。多亏了变量注释,现在才有可能。因此,如果您以前写过类似的内容:

MyType = namedtuple('MyType', ('a', 'b', 'c'))

现在您可以按如下定义它。 要添加新的构造函数,只需定义一个classmethod

from typing import NamedTuple


class MyType(NamedTuple):
    a: str
    b: int
    c: float

    @classmethod
    def from_string(cls, s):
        a, b, c = s.split()
        return cls(a, int(b), float(c))


print(MyType.from_string('1 2 3'))