簡體   English   中英

如何鍵入提示檢索動態枚舉值的方法?

[英]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]

這很好用,但是現在我正在添加類型提示,並且我正在努力為這些方法定義返回類型:我嘗試了sliceLiteral[] ,兩者都由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")應該是WordTypeget_enum_type("Number")應該是Number

正如其他人所說,您可以澄清它們將是枚舉。 您可以添加一個基本類型,並說它們將專門作為基本類型之一。 部分問題在於,盡管您可以 promise 它,但 MyPy 無法確認。 它無法知道inspect.getmembers(sys.modules[__name__], inspect.isclass)[1]中只會有EnumsGrammaticalEnum

如果您願意更改查找的實現,那么我建議您可以有利可圖地使用__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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM