[英]How to reference a python class attribute's type in a function signature
I have a common problem of referencing a class attribute type in a functions signature.我有一个在函数签名中引用 class 属性类型的常见问题。 Take for instance:
举个例子:
class Person:
name: str
def __init__(self, name) -> None:
self.name = name
def greet(name: str):
return "Hello " + name
In the snippet above I'd expect that I could do:在上面的代码片段中,我希望我能做到:
class Person:
name: str
def __init__(self, name) -> None:
self.name = name
def greet(name: Person.name):
return "Hello " + name
But this gives me an error pyright error Expected class type but received str
.但这给了我一个错误 pyright error
Expected class type but received str
。
I have gotten around it in a couple ways.我已经通过几种方式解决了这个问题。
PersonName = str
class Person:
name: PersonName
def __init__(self, name) -> None:
self.name = name
def greet(name: PersonName):
return "Hello " + name
reveal_type
.reveal_type
。class Person:
name: str
def __init__(self, name) -> None:
self.name = name
def greet(name: reveal_type[Person.name]):
return "Hello " + name
This feels like a simple thing to do but I've struggle to find a clean way.这感觉像是一件简单的事情,但我一直在努力寻找一种干净的方法。 What am I missing?
我错过了什么?
Edit: Thanks for the answers so far.编辑:感谢您到目前为止的回答。 I understand python typing doesn't parse
Person.name
.我了解 python 键入不解析
Person.name
。 I'm trying to understand if there is any syntax for accessing the type of a class attribute.我试图了解是否有任何语法可用于访问 class 属性的类型。 It appears
reveal_type
works but it feels like there should be a better way?看起来
reveal_type
有效,但感觉应该有更好的方法? For instance this is how it would be done in typescript.例如,这是在 typescript 中完成的方式。
class Person {
name: string;
}
function greet(name: Person["name"]) {
console.log(`hello ${name}`)
}
I think the other answers here are not wrong, but missing the actual point.我认为这里的其他答案并没有错,但没有抓住实际要点。
It seems to me that you just misunderstand, what it is you are actually doing, when you write name: Person.name
.在我看来,你只是误解了,当你写
name: Person.name
时,你实际上在做什么。 The right hand side is attribute access , meaning you are accessing the value of the name
attribute of Person
.右侧是属性访问,这意味着您正在访问
Person
的name
属性的值。
But Person
is a class and the way you defined it, that class has no attribute name
.但是
Person
是 class 并且您定义它的方式是 class没有属性name
。 It just has an annotation for name
.它只有
name
的注释。 You can see that if you just try and do the following:您可以看到,如果您只是尝试执行以下操作:
class Person:
name: str
print(Person.__annotations__)
print(Person.name)
The first print
will give you {'name': <class 'str'>}
, whereas the second will not even execute because Person.name
will raise an AttributeError: type object 'Person' has no attribute 'name'
.第一个
print
会给你{'name': <class 'str'>}
,而第二个甚至不会执行,因为Person.name
会引发AttributeError: type object 'Person' has no attribute 'name'
。
You could create that class attribute by assigning a value to name
in the class namespace:您可以通过为 class 命名空间中的
name
分配一个值来创建 class 属性:
class Person:
name: str = "foo"
Now Person.name
actually exists as an attribute.现在
Person.name
实际上作为一个属性存在。
But that would not solve your problem because that value is a string ie an instance of the str
class. Yet you want to annotate a variable with that, specifically you are doing this:但这不会解决您的问题,因为该值是一个字符串,即
str
class 的一个实例。但是您想要用它来注释一个变量,具体来说,您正在这样做:
def greet(name: Person.name): ...
Which would be equivalent to this:这相当于:
def greet(name: "foo"): ...
While syntactically correct, this makes no sense to any type checker because a variable annotation must be done with a type , not an instance.虽然在语法上是正确的,但这对任何类型检查器都没有意义,因为变量注释必须使用类型而不是实例来完成。 That is what pyright is telling you:
Expected class type but received str
.这就是 pyright 告诉您的内容:
Expected class type but received str
。 It sees that you are trying to annotate the name
function parameter with an instance of str
, but you should use an instance of type
(or a special typing construct such as Union
).它看到您正在尝试使用
str
的实例注释name
function 参数,但您应该使用type
的实例(或特殊类型构造,例如Union
)。
By the way, the type checker does not see what specific instance of str
is used in that annotation because it does not execute your code, it just reads it.顺便说一句,类型检查器看不到该注释中使用了
str
的哪个特定实例,因为它不执行您的代码,它只是读取它。 It does not know (or care) that the value at that point is always "foo"
.它不知道(或不关心)此时的值始终是
"foo"
。 The only thing it sees is that Person.name
is a str
instance and that is obviously not valid for type annotations.它唯一看到的是
Person.name
是一个str
实例,这显然对类型注释无效。
In your third code snippet you are technically just implicitly creating what is called a type alias by assigning PersonName = str
.在您的第三个代码片段中,您在技术上只是通过分配
PersonName = str
隐式创建所谓的类型别名。 Those are also totally fine to use in annotations, which is why there is no problem there.这些也完全可以用于注释,这就是为什么那里没有问题的原因。 The type checker just resolves both
name: PersonName
annotations (in the Person
class namespace and in the greet
function signature) to name: str
as in your very first snippet.类型检查器只是将
name: PersonName
注释(在Person
class 命名空间和greet
function 签名中)解析为name: str
,就像在您的第一个片段中一样。
To drive this point home, starting with Python 3.10
you could also do the following:要明确这一点,从 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)
Notice again that the name_type
attribute actually exists after the Person
class is fully defined.再次注意,在
Person
class 完全定义之后, name_type
属性实际存在。 Which means, by the time we get to the function definition, we can use that attribute to annotate the parameter.这意味着,当我们到达 function 定义时,我们可以使用该属性来注释参数。 (Though I find this approach quite ugly.)
(虽然我觉得这种方法很丑陋。)
Like @Karl Knechtel states in the comment, it is not how typing is designed to work.就像@Karl Knechtel 在评论中指出的那样,这并不是设计打字的方式。 You are describing an interface using typing, so if I import your package/class and try to call a function, I should know how to do so.
你正在使用类型描述一个接口,所以如果我导入你的包/类并尝试调用 function,我应该知道该怎么做。 It makes sense for custom classes you could define (
np.ndarray
or pd.DataFrame
are all over my code,), but if the interface for interacting with the class field is just str
you should be using that, and the variable name should tell me the rest. If you have specific additional restrictions for Person.name
making it not quack like a str
, it is a separate class. If not, use str
.这对于您可以定义的自定义类是有意义的(
np.ndarray
或pd.DataFrame
遍布我的代码,),但是如果与 class 字段交互的接口只是str
您应该使用它,并且变量名应该告诉我是 rest。如果您对Person.name
有特定的附加限制,使其不像str
那样嘎嘎作响,则它是一个单独的 class。如果不是,请使用str
。
On that note, if what you really want is defining a schema with possible additional validation and whatnot, you should look towards something like pydantic or marshmallow .关于这一点,如果您真正想要的是定义一个带有可能的额外验证的模式等等,您应该看看像pydantic或marshmallow之类的东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.