重复使用数据类类型提示

8

我想在函数签名中重用数据类中的类型提示 - 也就是说,不需要再次键入函数签名。

最佳方法是什么?

from dataclasses import dataclass
from typing import Set, Tuple, Type

@dataclass
class MyDataClass:
    force: Set[Tuple[str, float, bool]]

# I've had to write the same type annotation in the dataclass and the
# function signature - yuck
def do_something(force: Set[Tuple[str, float, bool]]):
    print(force)

# I want to do something like this, where I reference the type annotation from
# the dataclass. But, doing it this way, pycharm thinks `force` is type `Any`
def do_something_2(force: Type["MyDataClass.force"]):
    print(force)

6
你不能这样做。不过,你可以使用类型别名,GoodName = Set[Tuple[str, float, bool]],并重复使用它,GoodName - juanpa.arrivillaga
3
此外,这与数据类毫无关系。 - juanpa.arrivillaga
@juanpa.arrivillaga 感谢您提供的类型别名建议。我希望避免像那样创建辅助变量,但是确实是一种解决方案。这与数据类无关,只是为了说明问题而方便。 - fishstix44
1个回答

6
这个怎么做最好呢?
PEP 484在这种情况下提供了一个明确的选项。
类型别名是通过简单的变量赋值定义的: (...) 类型别名可以像注释中的类型提示一样复杂 - 任何在类型提示中被接受的东西都可以在类型别名中被接受。
应用到你的例子中,这意味着(Mypy确认为正确)。
from dataclasses import dataclass

Your_Type = set[tuple[str, float, bool]]


@dataclass
class MyDataClass:
    force: Your_Type


def do_something(force: Your_Type):
    print(force)

上述是使用 Python 3.9 或更新版本编写的通用别名类型。由于typing.Settyping.Tuple已被弃用,因此语法更加简洁和现代化。

现在,完全理解这个问题涉及到Python数据模型,可能比看起来更加复杂:

3.1. 对象、值和类型

每个对象都有一个身份、一种类型和一个值。

你第一次尝试使用 Type 会得到令人惊讶的结果。

>>> type(MyDataClass.force)

AttributeError: type object 'MyDataClass' has no attribute 'force'

这是因为内置函数type返回一个类型(它本身也是一个对象),但MyDataClass是“一个类”(一个声明),而“类属性”force在类上而不是类型对象上,type()在类型对象上查找。仔细观察数据模型中的区别:

  • 这些对象通常充当它们自己新实例的工厂

  • 类实例

    任意类的实例

如果您检查实例的类型,则会得到以下结果。

>>> init_values: set = {(True, "the_str", 1.2)}

>>> a_var = MyDataClass(init_values)

>>> type(a_var)
<class '__main__.MyDataClass'>

>>> type(a_var.force)
<class 'set'>

现在,让我们通过将type()应用于类声明对象上的__anotations__来恢复force的类型对象(而不是类型提示)(这里我们看到先前提到的通用别名类型)。 (在这里,我们确实检查了类属性force上的类型对象。)
>>> type(MyDataClass.__annotations__['force'])
<class 'typing._GenericAlias'>

或者我们可以检查类实例的注释,并像我们通常看到的那样恢复类型提示。
>>> init_values: set = {(True, "the_str", 1.2)}
>>> a_var = MyDataClass(init_values)
>>> a_var.__annotations__

{'force': set[tuple[str, float, bool]]}

我不得不在dataclass和函数签名中写相同的类型注释 -
对于元组注释往往变成长文字,这证明了为简洁起见创建一个目的变量。但是一般来说,显式签名更加描述性,这也是大多数API所采用的方式。 typing模块的基本构建块包括:
Tuple,通过列出元素类型使用,例如Tuple[int, int, str]。空元组可以被定义为Tuple[()]。可以使用一个类型和省略号表示任意长度的同构元组,例如Tuple[int, ...]。(这里的省略号是语法的一部分,是一个字面上的省略号。)

1
非常酷且富有洞见,谢谢! - fishstix44

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接