[英]How to type-hint that container subclass always contains particular type?
對於 PyCharm 中自定義容器子類的內容類型,我正在努力讓類型提示正常工作。 讓我們從一個符合我預期的案例開始。 您可以創建list
的子類並指定它始終具有int
內容,然后 Pycharm 會識別出此類列表中的每個項目都是int
。
class IntList(list[int]): pass
il = IntList(())
answer1 = il[0]
當我將鼠標懸停在answer1
上時,Pycharm 說它希望它具有int
類型,大概是因為類聲明指定IntList
不會是任何舊的list
,而是會是list[int]
。 (不要介意這個代碼在運行時會引發錯誤,因為il
會是空的。這只是一個最小的例子,表明 PyCharm 有時可以從類聲明中的括號[int]
中提取類型提示信息。同樣在從容器中獲取項目不會引發錯誤的其他情況下會出現問題。)
因此,當從list
子類化時,這工作得很好。 但我想做的是創建我自己的通用容器類——稱之為Box
它可以包含各種不同類型的對象。 然后我想聲明我自己的子類IntBox
,它只包含int
項,我希望 PyCharm 在其各種鼠標懸停提示、自動完成建議和 linting 錯誤檢測中識別這一點,就像它對IntList
。 所以這是我想要的一個非常精簡的例子。
class Box(list): pass
class IntBox(Box[int]): pass
ib = IntBox(())
answer2 = ib[0]
在這種情況下,當我將鼠標懸停在answer2
上時,PyCharm 說它可能具有類型Any
並且不承認[int]
暗示這不僅僅是一個通用的 Box/list,而是一個其內容已被類型提示的成為int
。
我已經嘗試了所有可以想象的變體,使用typing.TypeVar
和typing.Generic
來嘗試更明確地表明Box
的每個子類將具有單一類型的內容, Box.__getitem__
將返回該類型,並且對於類型為int
的子類IntBox
。
我發現的唯一解決方案是,當我創建ib
時,我可以顯式聲明此實例的類型為IntBox[int]
,然后 PyCharm 將知道期望ib[0]
將是一個int
。 但似乎我不需要每次創建一個IntBox
實例時都明確地說,而是應該有某種方法讓 PyCharm 從IntBox
的類聲明中的[int]
推斷這一點,就像它可以IntList
。
當然,這只是一個玩具示例。 在激發這一點的真實案例中,我希望我的通用容器類“Box”定義類型提示的其他方法(不僅僅是__getitem__
)以返回相關“Box”的子類始終包含的任何特定類型的對象,其中這因子類而異。 使用TypeVar
和Generic
我可以讓它工作,如果我明確地類型聲明每個子類實例將包含一個特定的[contenttype]
,但是如果沒有繁瑣的顯式類型聲明,我找不到讓它工作的方法每個實例。
編輯:由於在簡單的情況下工作的解決方案告訴list
子類中的元素顯然不會自動擴展到這個真實情況,這里有一個更接近我需要的例子,包括那個Box
是一個嵌套的Sequence
而不是一個簡單的list
,包括一個Box.get_first()
方法,它也應該接收IntBox
的int
類型提示,並且包括我認為大致正確使用TypeVar
的方法:
from typing import TypeVar, Sequence
T = TypeVar('T')
class Box(Sequence[Sequence[T]]):
def get_first(self:'Box[T]')->T:
return self[0][0]
class IntBox(Box[int]): pass
ib = IntBox() # works only if I declare this is type: IntBox[int]
answer = ib.get_first() # hovering over answer should show it will be int
進一步編輯:前面的問題似乎是嵌套的Sequence[Sequence[T]]
。 將其更改為Generic[T]
會使事情按預期工作。
使用typing.Generic
將類型從使用typing.TypeVar
的子類傳遞給Box
超類
from typing import Generic, TypeVar
T = TypeVar('T')
class Box(Generic[T], list[T]):
pass
class IntBox(Box[int]):
pass
class StrBox(Box[str]):
pass
ib = IntBox(())
answer1 = ib[0] # int
sb = StrBox(())
answer2 = sb[0] # str
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.