定义一个行为类似于 typing.Any 的自定义类型

Define a custom Type that behaves like typing.Any

我需要创建一个类型,当类型检查器 (mypy) 查看时,它的行为类似于 typing.Any,但与 typing.Any 不同。

用例是一些漂亮的 "meta" 代码,需要从一组可以用 typing.Any 注释的其他变量中找到用这种类型注释的变量。 请注意,我永远不必实际创建此类型的实例,我只需要它用于数据类上下文中的类型注释。 示例:

from dataclasses import dataclass, fields
from typing import Any


MyAny = ... # What to put here?

@dataclass()
class Test:

    a: Any
    b: MyAny = None


for field in fields(Test):
    if field.type == MyAny:
        print(f"It's {field.name}")   # This should print "It's b"

我尝试过的事情:

  1. 不起作用,因为您不能继承 Any:TypeError: Cannot subclass <class 'typing._SpecialForm'>

    class MyAny(Any):
       pass
    
  2. 不起作用,因为它无法与普通的 Any 区分开来(上面截取的代码的结果是 It's a\nIt's b

    MyAny = Any
    
  3. 在运行时工作,但 mypy 抱怨默认值: Mypy: Incompatible types in assignment (expression has type "None", variable has type "MyAny")

    class MyAny:
       pass
    
  4. 在运行时工作,但 mypy 无法判断这应该表现得像 Any: 它抱怨的定义是 Mypy: Argument 2 to NewType(...) must be subclassable(got "Any") 它抱怨默认参数: Mypy: Incompatible types in assignment (expression has type "None", variable has type "MyAny")

    from typing import NewType
    MyAny = NewType("MyAny", Any)
    

那么有什么办法可以做到这一点吗?

您可以使用 TypeVar

# foo.py
from dataclasses import dataclass, fields
from typing import Any, TypeVar, Generic, Optional


MyAny = TypeVar('MyAny')

@dataclass()
class Test(Generic[MyAny]):

    a: Any
    b: Optional[MyAny] = None

for field in fields(Test):
    if field.type == Optional[MyAny]:
        print(f"It's {field.name}")

输出

$ python3 foo.py 
It's b

$ mypy foo.py 
Success: no issues found in 1 source file

您可以使用条件来欺骗 mypy 解释一段代码,同时让您的运行时执行另一段代码。

from dataclasses import dataclass, fields
from typing import Any


if False:
    MyAny = Any
else:
    class MyAny:  # type: ignore
        pass


@dataclass()
class Test:

    a: Any
    b: MyAny = None


for field in fields(Test):
    if field.type == MyAny:
        print(f"It's {field.name}")   # This should print "It's b"