簡體   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