【问题标题】:Python typing support for NamedTupleNamedTuple 的 Python 类型支持
【发布时间】:2020-02-27 09:36:15
【问题描述】:

请检查以下代码

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,这样我就表明任何子类都应该通过返回由intstr 字段组成的NamedTuple 来覆盖它。

但我收到一条错误消息:

TypeError: 'NamedTupleMeta' object is not subscriptable

如下更改签名会有所帮助,但我将如何告诉打字系统子类可以返回只有 int 和 str 的 NamedTuples

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

【问题讨论】:

  • 你不能也使用NT 作为A 类中的类型吗?
  • @Dan 不过,大概是 OP 希望能够使用 class Foo(NamedTuple): bar: int,然后那将无法正常工作
  • 现在,我对类型系统的了解并不多,但是静态类型检查器如何使用它呢?如果你只知道它是某种类型,它具有一定数量的typing.Union[str, int] 属性,那么你仍然不得不抱怨foo.bar,因为该类型不知道 bar...

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


【解决方案1】:

问题在于基本上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)

但是,使用 Tuple 会:

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

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

真正想要的是某种结构类型,但我想不出任何方法可以为此使用typing.Protocol。基本上,它不能表达“任何具有可变数量属性的类型,所有这些属性都是typing.Union[int, str]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-04-05
    • 2017-06-19
    • 2020-12-04
    • 2013-12-08
    • 2018-04-27
    • 1970-01-01
    • 2019-01-31
    相关资源
    最近更新 更多