【问题标题】:Type hints in namedtuple在 namedtuple 中输入提示
【发布时间】:2016-03-20 02:17:25
【问题描述】:

考虑以下代码:

from collections import namedtuple
point = namedtuple("Point", ("x:int", "y:int"))

上面的代码只是展示我想要实现的目标的一种方式。 我想用类型提示制作namedtuple

你知道任何优雅的方式来达到预期的结果吗?

【问题讨论】:

标签: python type-hinting python-typing namedtuple python-dataclasses


【解决方案1】:

自 3.6 以来类型化命名元组的首选语法是

from typing import NamedTuple

class Point(NamedTuple):
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)

编辑 从 Python 3.7 开始,考虑使用 dataclasses(您的 IDE 可能还不支持它们进行静态类型检查):

from dataclasses import dataclass

@dataclass
class Point:
    x: int
    y: int = 1  # Set default value

Point(3)  # -> Point(x=3, y=1)

【讨论】:

  • @JohnE; OP 专门要求命名元组。是的,数据类将更好地服务于命名元组的许多用例。但是引用优秀的Why not namedtuples如果你想要一个有名字的元组,无论如何:去一个命名的元组
  • 使用数据类,不可能像元组那样解构生成的对象
  • 元组是不可变的。数据类不是(默认情况下)它确实具有接近元组行为的冻结标志。只是需要注意的事情。
  • 如果数据类适合您,您可以更进一步,使用pydantic 包以优雅的方式在运行时强制执行类型检查。
  • 数据类是不可订阅的,并且在迭代命名元组时它们也不是不可打包的,所以我认为它们远非完美的选择。
【解决方案2】:

您可以使用typing.NamedTuple

来自文档

键入的版本namedtuple

>>> import typing
>>> Point = typing.NamedTuple("Point", [('x', int), ('y', int)])

这仅在 Python 3.5 及更高版本中存在

【讨论】:

  • 我这样声明:GeoPoint = NamedTuple('GeoPoint', [('longitude', float), ('latitude', float)]) 然后我尝试 geo = GeoPoint(**data) 其中数据是包含所需键和值的字典,为 decimal.Decimal,并且不会发生浮点转换;(也没有打字错误:( :( 那么typing.NamedTuple 是如何工作的?见gist.github.com/andilabs/15002176b2bda786b9037077fa06cc71
  • @andi 键入不强制或强制转换变量,afaik。
  • 在较新的版本中,您可以将 NamedTuples 声明为 Point = typing.NamedTuple("Point", x=int, y=int),这样更简洁、更短。
【解决方案3】:

公平地说,NamedTuple 来自typing

>>> from typing import NamedTuple
>>> class Point(NamedTuple):
...     x: int
...     y: int = 1  # Set default value
...
>>> Point(3)
Point(x=3, y=1)

等于经典namedtuple:

>>> from collections import namedtuple
>>> p = namedtuple('Point', 'x,y', defaults=(1, ))
>>> p.__annotations__ = {'x': int, 'y': int}
>>> p(3)
Point(x=3, y=1)

所以,NamedTuple 只是 namedtuple 的语法糖

下面,你可以从python 3.10的源代码中找到一个创建NamedTuple的函数。如我们所见,它使用collections.namedtuple 构造函数并从提取的类型中添加__annotations__

def _make_nmtuple(name, types, module, defaults = ()):
    fields = [n for n, t in types]
    types = {n: _type_check(t, f"field {n} annotation must be a type")
             for n, t in types}
    nm_tpl = collections.namedtuple(name, fields,
                                    defaults=defaults, module=module)
    nm_tpl.__annotations__ = nm_tpl.__new__.__annotations__ = types
    return nm_tpl

【讨论】:

  • 语法糖是 解析器 可以用更基本的语法代替的东西。 NamedTuple 比这复杂一点,它是一个在运行时实际执行某些操作的函数。
  • 是的,我知道它在运行时做了什么。它正在提取类型并将它们添加到使用构造函数collections.namedtuple 刚刚创建的namedtuple__annotations__ attr。为了更好地理解,我将该代码添加到答案中。
猜你喜欢
  • 2019-10-19
  • 2013-01-25
  • 1970-01-01
  • 1970-01-01
  • 2022-07-20
  • 2017-04-03
  • 2019-11-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多