[英]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
。
我已經通過幾種方式解決了這個問題。
PersonName = str
class Person:
name: PersonName
def __init__(self, name) -> None:
self.name = name
def greet(name: PersonName):
return "Hello " + name
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
時,你實際上在做什么。 右側是屬性訪問,這意味着您正在訪問Person
的name
屬性的值。
但是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.ndarray
或pd.DataFrame
遍布我的代碼,),但是如果與 class 字段交互的接口只是str
您應該使用它,並且變量名應該告訴我是 rest。如果您對Person.name
有特定的附加限制,使其不像str
那樣嘎嘎作響,則它是一個單獨的 class。如果不是,請使用str
。
關於這一點,如果您真正想要的是定義一個帶有可能的額外驗證的模式等等,您應該看看像pydantic或marshmallow之類的東西。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.