Проблема в том, что принципиально 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, практичность важнее чистоты.
И обратите внимание, люди часто путаются с 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]
.
person
juanpa.arrivillaga
schedule
27.02.2020
NT
как тип в классеA
? - person Dan   schedule 27.02.2020class Foo(NamedTuple): bar: int
, и тогда это не сработало бы - person juanpa.arrivillaga   schedule 27.02.2020typing.Union[str, int]
, вам все равно придется жаловаться наfoo.bar
, потому что этот тип не знает о bar... - person juanpa.arrivillaga   schedule 27.02.2020