请检查以下代码

import typing
import abc


class A(abc.ABC):
    @abc.abstractmethod
    def f(self) -> typing.NamedTuple[typing.Union[int, str], ...]:
        ...


class NT(typing.NamedTuple):
    a: int
    b: str


class B(A):

    def f(self) -> NT:
        return NT(1, "s")


print(B().f())


我得到一个错误。在父类A中,我想定义方法f,以便指示任何子类都应通过返回仅由NamedTuple ot int字段组成的str来覆盖它。

但我得到一个错误说:

TypeError: 'NamedTupleMeta' object is not subscriptable


如下更改签名很有帮助,但是然后我将如何告诉打字系统子类可以返回仅具有int和str的NamedTuples。

class A(abc.ABC):
    @abc.abstractmethod
    def f(self) -> typing.NamedTuple:
        ...

最佳答案

问题是,基本上typing.NamedTuple不是正确的类型。实际上,它允许您使用继承和类型注释的语法来使用类工厂collections.namedtuple。是糖

这是误导。通常,当我们期望:

class Foo(Bar):
    pass

foo = Foo()
print(isinstance(foo, Bar))


始终打印True。但是typing.NamedTuple实际上,通过元类机制,使得tuple的后代变得完全像collections.namedtuple一样。实际上,实际上存在的唯一原因是使用NamedTupleMetaclass拦截类的创建。也许以下内容会启发您:

>>> from typing import NamedTuple
>>> class Employee(NamedTuple):
...     """Represents an employee."""
...     name: str
...     id: int = 3
...
>>> isinstance(Employee(1,2), NamedTuple)
False
>>>
>>> isinstance(Employee(1,2), tuple)
True


有些人可能觉得这很脏,但是正如Python Zen所述,实用性胜过纯洁。

请注意,人们常常对collections.namedtuple感到困惑,它本身不是一个类,而是一个类工厂。所以:

>>> import collections
>>> Point = collections.namedtuple("Point", "x y")
>>> p = Point(0, 0)
>>> isinstance(p, collections.namedtuple)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: isinstance() arg 2 must be a type or tuple of types


请注意,由namedtuple / NamedTuple生成的类在从它们继承时确实按预期方式工作。

注意,您的解决方案:

import typing
import abc


class A(abc.ABC):
    @abc.abstractmethod
    def f(self) -> typing.Tuple:
        ...


class NT(typing.NamedTuple):
    a: int
    b: str


class B(A):

    def f(self) -> NT:
        return NT(1, "s")


print(B().f())


不通过mypy:

(py38) juan$ mypy test_typing.py
test_typing.py:18: error: Return type "NT" of "f" incompatible with return type "NamedTuple" in supertype "A"
Found 1 error in 1 file (checked 1 source file)


但是,usint Tuple可以:

class A(abc.ABC):
    @abc.abstractmethod
    def f(self) -> typing.Tuple[typing.Union[str, int],...]:
        ...


虽然,这可能不是很有用。

您真正想要的是某种结构化类型,但是我想不出任何使用typing.Protocol的方式。基本上,它不能表示“具有各种可变属性的所有类型都是typing.Union[int, str]的任何类型。

关于python - Python对NamedTuple的输入支持,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/60430110/

10-12 07:39
查看更多