繁体   English   中英

如何在 function 签名中引用 python class 属性的类型

[英]How to reference a python class attribute's type in a function signature

我有一个在函数签名中引用 class 属性类型的常见问题。 举个例子:

class Person:
    name: str 

    def __init__(self, name) -> None:
        self.name = name 

def greet(name: str):
    return "Hello " + name

在上面的代码片段中,我希望我能做到:

class Person:
    name: str 

    def __init__(self, name) -> None:
        self.name = name 

def greet(name: Person.name):
    return "Hello " + name

但这给了我一个错误 pyright error Expected class type but received str

我已经通过几种方式解决了这个问题。

  1. 定义一个类型并在两个实例中使用它。
PersonName = str

class Person:
    name: PersonName 

    def __init__(self, name) -> None:
        self.name = name 

def greet(name: PersonName):
    return "Hello " + name
  1. 使用reveal_type
class Person:
    name: str

    def __init__(self, name) -> None:
        self.name = name 

def greet(name: reveal_type[Person.name]):
    return "Hello " + name

这感觉像是一件简单的事情,但我一直在努力寻找一种干净的方法。 我错过了什么?

编辑:感谢您到目前为止的回答。 我了解 python 键入不解析Person.name 我试图了解是否有任何语法可用于访问 class 属性的类型。 看起来reveal_type有效,但感觉应该有更好的方法? 例如,这是在 typescript 中完成的方式。

class Person {
  name: string;
}

function greet(name: Person["name"]) {
  console.log(`hello ${name}`)
}

我认为这里的其他答案并没有错,但没有抓住实际要点。

在我看来,你只是误解了,当你写name: Person.name时,你实际上在做什么。 右侧是属性访问,这意味着您正在访问Personname属性的

但是Person是 class 并且您定义它的方式是 class没有属性name 它只有name注释 您可以看到,如果您只是尝试执行以下操作:

class Person:
    name: str

print(Person.__annotations__)
print(Person.name)

第一个print会给你{'name': <class 'str'>} ,而第二个甚至不会执行,因为Person.name会引发AttributeError: type object 'Person' has no attribute 'name'


可以通过为 class 命名空间中的name分配一个值来创建 class 属性:

class Person:
    name: str = "foo"

现在Person.name实际上作为一个属性存在。

但这不会解决您的问题,因为该值是一个字符串,即str class 的一个实例。但是您想要用它来注释一个变量,具体来说,您正在这样做:

def greet(name: Person.name): ...

这相当于:

def greet(name: "foo"): ...

虽然在语法上是正确的,但这对任何类型检查器都没有意义,因为变量注释必须使用类型而不是实例来完成。 就是 pyright 告诉您的内容: Expected class type but received str 它看到您正在尝试使用str的实例注释name function 参数,但您应该使用type的实例(或特殊类型构造,例如Union )。

顺便说一句,类型检查器不到该注释中使用了str的哪个特定实例,因为它不执行您的代码,它只是读取它。 它不知道(或不关心)此时的值始终是"foo" 它唯一看到的是Person.name是一个str实例,这显然对类型注释无效。


在您的第三个代码片段中,您在技术上只是通过分配PersonName = str隐式创建所谓的类型别名 这些也完全可以用于注释,这就是为什么那里没有问题的原因。 类型检查器只是将name: PersonName注释(在Person class 命名空间和greet function 签名中)解析为name: str ,就像在您的第一个片段中一样。

要明确这一点,从 Python 3.10开始,您还可以执行以下操作:

from typing import TypeAlias


class Person:
    name_type: TypeAlias = str
    name: name_type


def greet(name: Person.name_type) -> None:
    print("Hi", name)

再次注意,在Person class 完全定义之后, name_type属性实际存在。 这意味着,当我们到达 function 定义时,我们可以使用该属性来注释参数。 (虽然我觉得这种方法很丑陋。)

就像@Karl Knechtel 在评论中指出的那样,这并不是设计打字的方式。 你正在使用类型描述一个接口,所以如果我导入你的包/类并尝试调用 function,我应该知道该怎么做。 这对于您可以定义的自定义类是有意义的( np.ndarraypd.DataFrame遍布我的代码,),但是如果与 class 字段交互的接口只是str您应该使用它,并且变量名应该告诉我是 rest。如果您对Person.name有特定的附加限制,使其不像str那样嘎嘎作响,则它是一个单独的 class。如果不是,请使用str

关于这一点,如果您真正想要的是定义一个带有可能的额外验证的模式等等,您应该看看像pydanticmarshmallow之类的东西。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM