[英]How to type-hint a method that retrieves dynamic enum value?
我有一個 Python 模塊,它有許多簡單的枚舉,定義如下:
class WordType(Enum):
ADJ = "Adjective"
ADV = "Adverb"
class Number(Enum):
S = "Singular"
P = "Plural"
因為有很多這樣的枚舉,而我只在運行時決定哪些枚舉來查詢任何給定的輸入,所以我想要一個 function 可以檢索給定枚舉類型和枚舉值作為字符串的值。 我成功地做到了如下:
names = inspect.getmembers(sys.modules[__name__], inspect.isclass)
def get_enum_type(name: str):
enum_class = [x[1] for x in names if x[0] == name]
return enum_class[0]
def get_enum_value(object_name: str, value_name: str):
return get_enum_type(object_name)[value_name]
這很好用,但是現在我正在添加類型提示,並且我正在努力為這些方法定義返回類型:我嘗試了slice
和Literal[]
,兩者都由mypy
建議,但都沒有檢查出來(可能是因為我不明白我可以給Literal[]
什么類型參數)。
我願意修改枚舉定義,但我更願意保持動態查詢不變。 最壞的情況,我可以做# type: ignore
或只是 return -> Any
,但我希望有更好的東西。
由於您不想檢查任何Enum
的類型,我建議引入一個基本類型(例如GrammaticalEnum
)來標記所有Enum
並將它們分組到一個自己的模塊中:
# module grammar_enums
import sys
import inspect
from enum import Enum
class GrammaticalEnum(Enum):
"""use as a base to mark all grammatical enums"""
pass
class WordType(GrammaticalEnum):
ADJ = "Adjective"
ADV = "Adverb"
class Number(GrammaticalEnum):
S = "Singular"
P = "Plural"
# keep this statement at the end, as all enums must be known first
grammatical_enums = dict(
m for m in inspect.getmembers(sys.modules[__name__], inspect.isclass)
if issubclass(m[1], GrammaticalEnum))
# you might prefer the shorter alternative:
# grammatical_enums = {k: v for (k, v) in globals().items()
# if inspect.isclass(v) and issubclass(v, GrammaticalEnum)}
關於打字,yakir0 已經建議了正確的類型,
但有了共同的基礎,你可以縮小它們。
如果你願意,你甚至可以完全擺脫你的功能:
from grammar_enums import grammatical_enums as g_enums
from grammar_enums import GrammaticalEnum
# just use g_enums instead of get_enum_value like this
WordType_ADJ: GrammaticalEnum = g_enums['WordType']['ADJ']
# ...or use your old functions:
# as your grammatical enums are collected in a dict now,
# you don't need this function any more:
def get_enum_type(name: str) -> Type[GrammaticalEnum]:
return g_enums[name]
def get_enum_value(enum_name: str, value_name: str) -> GrammaticalEnum:
# return get_enum_type(enum_name)[value_name]
return g_enums[enum_name][value_name]
您始終可以運行您的函數並打印 function 的結果,以了解它應該是什么。 請注意,您可以像任何其他 class 一樣在類型提示中使用Enum
。
例如:
>>> result = get_enum_type('WordType')
... print(result)
... print(type(result))
<enum 'WordType'>
<class 'enum.EnumMeta'>
所以你實際上可以使用
get_enum_type(name: str) -> EnumMeta
但是你可以通過使用Type
from typing
使它更漂亮,因為EnumMeta
是一般 Enum 的類型。
get_enum_type(name: str) -> Type[Enum]
對於與get_enum_value
類似的過程,您會得到
>>> type(get_enum_value('WordType', 'ADJ'))
<enum 'WordType'>
顯然,您不會總是返回WordType
類型,因此您可以使用Enum
來概括返回類型。
總結一下:
get_enum_type(name: str) -> Type[Enum]
get_enum_value(object_name: str, value_name: str) -> Enum
正如我在評論中所說,我認為不可能擁有您的動態代碼並讓 MyPy 預測輸出。 例如,我認為不可能讓 MyPy 知道get_enum_type("WordType")
應該是WordType
而get_enum_type("Number")
應該是Number
。
正如其他人所說,您可以澄清它們將是枚舉。 您可以添加一個基本類型,並說它們將專門作為基本類型之一。 部分問題在於,盡管您可以 promise 它,但 MyPy 無法確認。 它無法知道inspect.getmembers(sys.modules[__name__], inspect.isclass)
在[1]
中只會有Enums
或GrammaticalEnum
。
如果您願意更改查找的實現,那么我建議您可以有利可圖地使用__init_subclass__
。 就像是
GRAMMATICAL_ENUM_LOOKUP: "Mapping[str, GrammaticalEnum]" = {}
class GrammaticalEnum(Enum):
def __init_subclass__(cls, **kwargs):
GRAMMATICAL_ENUM_LOOKUP[cls.__name__] = cls
super().__init_subclass__(**kwargs)
def get_enum_type(name: str) -> Type[GrammaticalEnum]:
return GRAMMATICAL_ENUM_LOOKUP[name]
這至少有一個優點,即 MyPy 可以看到正在發生的事情,並且應該對此感到滿意。 它知道所有內容實際上都是有效的GrammaticalEnum
,因為這就是GRAMMATICAL_ENUM_LOOKUP
填充的全部內容。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.