在创建实例之前修改参数以键入.NamedTuple

Ric*_*ica 6 python typing namedtuple

我期待使用有点新的typing.NamedTuple类,它允许使用通常的Python类语法创建命名元组类(包括添加文档字符串和方法,提供默认值,类型提示等等).

但是:底部的类产生以下错误消息:

AttributeError: Cannot overwrite NamedTuple attribute __new__
Run Code Online (Sandbox Code Playgroud)

从中我收集它所说的内容:压倒一切__new__仍然是禁忌.这非常令人失望.

解决这个问题的"旧方法"是从一个命名的元组类继承,但这需要我认为是一些丑陋的样板代码:

from collections import namedtuple

class FormatSpec(namedtuple('FormatSpecBase', 'fill align sign alt zero '
                                              'width comma decimal precision type')):
    __slots__ = ()
    def __new__(cls, fill, align, sign, alt, zero,
                width, comma, decimal, precision, type):
        to_int=lambda x: int(x) if x is not None else x
        zero=to_int(zero)
        width=to_int(width)
        precision=to_int(precision)
        return super().__new__(cls, fill, align, sign, alt, zero,
                               width, comma, decimal, precision, type)

FormatSpec.__doc__=_FormatSpec.__doc__.replace('FormatSpecBase','FormatSpec')
Run Code Online (Sandbox Code Playgroud)

是否有一些其他的替代方法可以让我投的zero,width和precision论据下面int之前创建一个名为元组的,但仍然使用相同的类创建语法?还是我坚持使用旧的方式?

from typing import NamedTuple, Optional

class FormatSpec(NamedTuple):
    """Represents a string that conforms to the [Format Specification
    Mini-Language][1] in the string module.

    [1]: https://docs.python.org/3/library/string.html#formatspec
    """
    fill: Optional[str]
    align: Optional[str]
    sign: Optional[str]
    alt: Optional[str]
    zero: Optional[int]
    width: Optional[int]
    comma: Optional[str]
    decimal: Optional[str]
    precision: Optional[int]
    type: str
    def __new__(cls, fill, align, sign, alt, zero, width, comma, decimal, precision, type):
        to_int=lambda x: int(x) if x is not None else x
        zero=to_int(zero)
        width=to_int(width)
        precision=to_int(precision)
        return super().__new__(cls, fill, align, sign, alt, zero,
                               width, comma, decimal, precision, type)
    def join(self):
        return ''.join('{!s}'.format(s) for s in self if s is not None)
    def __format__(self, format_spec):
        try:
            return format(self.join(), format_spec)
        except (TypeError, ValueError):
            return super().__format__(format_spec)
Run Code Online (Sandbox Code Playgroud)

Ric*_*ica 1

一种方法是将其分为两个类,并在子类中进行参数修改:

from typing import NamedTuple, Optional

class FormatSpecBase(NamedTuple):
    """Represents a string that conforms to the [Format Specification
    Mini-Language][1] in the string module.

    [1]: https://docs.python.org/3/library/string.html#formatspec
    """
    fill: Optional[str]
    align: Optional[str]
    sign: Optional[str]
    alt: Optional[str]
    zero: Optional[int]
    width: Optional[int]
    comma: Optional[str]
    decimal: Optional[str]
    precision: Optional[int]
    type: str
    def join(self):
        return ''.join('{!s}'.format(s) for s in self if s is not None)
    def __format__(self, format_spec):
        try:
            return format(self.join(), format_spec)
        except (TypeError, ValueError):
            return super().__format__(format_spec)


class FormatSpec(FormatSpecBase):
    __slots__ = ()
    def __new__(cls, fill, align, sign, alt, zero, width, comma, decimal, precision, type):
        to_int=lambda x: int(x) if x is not None else x
        zero=to_int(zero)
        width=to_int(width)
        precision=to_int(precision)
        return super().__new__(cls, fill, align, sign, alt, zero,
                                    width, comma, decimal, precision, type)
Run Code Online (Sandbox Code Playgroud)

我不太喜欢这种方法,但至少它比“旧方法”更具可读性(尽管它仍然需要那些悬而未决的__slots__废话)。

另一种方式是工厂:

def MakeFormatSpec(cls, fill, align, sign, alt, zero,
                   width, comma, decimal, precision, type):
    to_int=lambda x: int(x) if x is not None else x
    zero=to_int(zero)
    width=to_int(width)
    precision=to_int(precision)
    return FormatSpec(fill, align, sign, alt, zero,
                      width, comma, decimal, precision, type)

fspec = MakeFormatSpec(*parse_format_spec(some_format_spec_string))
Run Code Online (Sandbox Code Playgroud)

...或工厂方法:

    @classmethod
    def make(cls, fill, align, sign, alt, zero, width, comma, decimal, precision, type):
        to_int=lambda x: int(x) if x is not None else x
        zero=to_int(zero)
        width=to_int(width)
        precision=to_int(precision)
        return cls(fill, align, sign, alt, zero,
                   width, comma, decimal, precision, type)

fspec = FormatSpec.make(*parse_format_spec(some_format_spec_string))
Run Code Online (Sandbox Code Playgroud)

然而,与简单地能够做到的相比,这些都相当笨重:

fspec = FormatSpec(*parse_format_spec(some_format_spec_string))
Run Code Online (Sandbox Code Playgroud)