Python:键入注释函数返回值,该值返回该函数中定义的类

Python: type annotate a function return value that returns a class defined in that function

提问人:Joe J 提问时间:10/27/2023 最后编辑:Joe J 更新时间:10/27/2023 访问量:50

问:

我想知道对函数或方法的返回值进行类型注释的正确方法是什么,其中类是在该函数中定义的。

例如:

from typing import List
from dataclasses import dataclass

def get_users() -> List['ReturnUser']:

    @dataclass
    class ReturnUser:
       first_name: str
       last_name: str

    return [
        ReturnUser("John", "Doe"), 
        ReturnUser("Jane", "Doe")]

...ReturnUser 数据类仅在函数范围内相关。(我不关心函数之外的对象,除了我可以在调用代码中访问它的属性)。get_users()

但是,Pylance 将返回值注释显示为类型错误。不幸的是,我的环境是 Python 3.7,但也很好奇较新的 Python 版本中是否存在更新的方法?List['ReturnUser']

如何注释尚不存在的返回值?

类型注解 python-3.12

评论


答:

3赞 Silvio Mayolo 10/27/2023 #1

不仅不存在,而且每次都是不同的类。我们可以很容易地验证这一点。ReturnUser

>>> a = get_users()
>>> b = get_users()
>>> type(a[0]) is type(b[0])
False

所以我的第一个建议是不要这样做。创建一个模块级类,并在其前加上下划线,以指示它是私有的。在 Python 中定义 a 内部的充分理由非常非常少classdef

话虽如此,让我们假设这确实是唯一的方法。假设这里没有显示一些设计约束,并且您无法摆脱该类模式。正如我所说,它不是正确的返回类型,因为该函数每次都返回不同(不相关)类的值。如果您希望函数的返回类型是“带有 and 字段的东西”,我们可以使用 Protocol 来做到这一点。ReturnUserfirst_namelast_name

class _ReturnUserLike(Protocol):
    first_name: str
    last_name: str

def get_users() -> List[_ReturnUserLike]:
    # ... Same class body, including the local class ...

现在,对调用方的唯一类型级保证是您具有属性,并且这些属性是字符串。A 是一种结构类型,这意味着出于类型检查的目的,任何看起来像该类的东西都算作实例。first_namelast_nameProtocol

评论

0赞 Joe J 10/27/2023
感谢您对此的回复和建议。我对数据类返回值的想法是,它将是返回数据的短期“持有者”,并且比在元组或字典中返回该数据更具表现力和更好的记录。但是,我认为您将其定义为全局对象的意思,否则它会混淆相等的定义,并且可能只会让该函数的调用者感到困惑(因为他们不知道该对象是什么)。也感谢使用 Protocol 的示例;我也会读到这一点。
1赞 Nikolaj Š. 10/27/2023
@JoeJ:既然你提到要考虑元组,那么对于你的用例来说,命名元组可能比数据类更好。请参阅数据类与类型。NamedTuple 主要用例
0赞 Silvio Mayolo 10/27/2023
不客气!有了补充的信息,我同意尼古拉的观点;我以前遇到过这种情况,这是一个很好的中间地带:它有用于组织和调试目的的命名字段,但可以在调用站点进行解构,就像元组一样。NamedTuple