簡體   English   中英

Python:如何檢查定義的types.UnionType中有哪些類型?

[英]Python: How to check what types are in defined types.UnionType?

我正在使用 Python 3.11,我需要檢測可選的class屬性是否為 Enum 類型(即 Enum 的子類類型)。

使用typing.get_type_hints()我可以獲得類型提示作為字典,但是如何檢查字段的類型是否是可選的枚舉(子類)? 如果我能得到任何可選字段的類型就更好了,不管它是Optional[str]Optional[int]Optional[Class_X]等等。

示例代碼

from typing import Optional, get_type_hints
from enum import IntEnum, Enum

class TestEnum(IntEnum):
    foo = 1
    bar = 2


class Foo():
    opt_enum : TestEnum | None = None

types = get_type_hints(Foo)['opt_enum']

這有效

(ipython)

In [4]: Optional[TestEnum] == types
Out[4]: True

這些失敗了

(是的,這些都是絕望的嘗試)

In [6]: Optional[IntEnum] == types
Out[6]: False

In [11]: issubclass(Enum, types)
Out[11]: False

In [12]: issubclass(types, Enum)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [12], line 1
----> 1 issubclass(types, Enum)

TypeError: issubclass() arg 1 must be a class

In [13]: issubclass(types, Optional[Enum])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [13], line 1
----> 1 issubclass(types, Optional[Enum])

File /usr/lib/python3.10/typing.py:1264, in _UnionGenericAlias.__subclasscheck__(self, cls)
   1262 def __subclasscheck__(self, cls):
   1263     for arg in self.__args__:
-> 1264         if issubclass(cls, arg):
   1265             return True

TypeError: issubclass() arg 1 must be a class

In [7]: IntEnum in types
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In [7], line 1
----> 1 IntEnum in types

TypeError: argument of type 'types.UnionType' is not iterable

為什么我需要這個

我有幾種情況,我從 csv 文件導入數據並從每一行創建 class 的對象。 csv.DictReader()返回一個dict[str, str] ,我需要在嘗試創建 object 之前修復字段的類型。但是,一些 object 字段是Optional[int]Optional[bool]Optional[EnumX]Optional[ClassX] 我有幾個類繼承了我的CSVImportable()類/接口。 我想在CSVImportable() class 中實現一次邏輯,而不是在每個子類中以字段感知方式編寫大致相同的代碼。 這個CSVImportable._field_type_updater()應該:

  1. 至少為基本類型和枚舉正確更改類型
  2. 優雅地跳過Optional[ClassX]字段

當然,我也感謝更好的設計:-)

當您處理參數化類型(通用或特殊類型,如typing.Optional )時,您可以通過get_args / get_origin檢查它。

這樣做你會看到T | S T | S的實現方式與typing.Union[T, S]略有不同。 前者的起源types.UnionType ,而后者的起源是typing.Union 不幸的是,這意味着要涵蓋這兩種變體,我們需要進行兩次不同的檢查。

from types import UnionType
from typing import Union, get_origin

def is_union(t: object) -> bool:
    origin = get_origin(t)
    return origin is Union or origin is UnionType

使用typing.Optional只是在引擎蓋下使用typing.Union ,所以起源是一樣的。 這是一個工作演示:

from enum import IntEnum
from types import UnionType
from typing import Optional, get_type_hints, get_args, get_origin, Union


class TestEnum(IntEnum):
    foo = 1
    bar = 2


class Foo:
    opt_enum1: TestEnum | None = None
    opt_enum2: Optional[TestEnum] = None
    opt_enum3: TestEnum
    opt4: str


def is_union(t: object) -> bool:
    origin = get_origin(t)
    return origin is Union or origin is UnionType


if __name__ == "__main__":
    for name, type_ in get_type_hints(Foo).items():
        if type_ is TestEnum or is_union(type_) and TestEnum in get_args(type_):
            print(name, "accepts TestEnum")

Output:

opt_enum1 accepts TestEnum
opt_enum2 accepts TestEnum
opt_enum3 accepts TestEnum

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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