如何在Python中创建自己的“参数化”类型(如`Optional [T]`)?

问题描述:

我想在Python中创建自己的参数化类型为类型提示使用:如何在Python中创建自己的“参数化”类型(如`Optional [T]`)?

class MaybeWrapped: 
    # magic goes here 

T = TypeVar('T') 

assert MaybeWrapped[T] == Union[T, Tuple[T]] 

不要介意人为的例子;我怎样才能实现这个?我查看了Union和Optional的源代码,但它看起来像是我想避免的一些相当低级的hackery。

文档中的唯一建议来自example re-implementation of Mapping[KT,VT] that inherits from Generic。但是这个例子更多的是关于__getitem__方法而不是关于类本身。

如果您只是试图创建泛型类或函数,请尝试看一下documentation on mypy-lang.org about generic types - 这是相当全面的,并且更详细,然后是标准库打字文档。

如果你想实现你的具体的例子,这是值得指出的是type aliases work with typevars - 你可以简单地做:

from typing import Union, TypeVar, Tuple 

T = TypeVar('T') 

MaybeWrapped = Union[T, Tuple[T]] 

def foo(x: int) -> MaybeWrapped[str]: 
    if x % 2 == 0: 
     return "hi" 
    else: 
     return ("bye",) 

# When running mypy, the output of this line is: 
# test.py:13: error: Revealed type is 'Union[builtins.str, Tuple[builtins.str]]' 
reveal_type(foo(3)) 

不过,如果你想建立一个泛型类型与真正的新语义学,你很可能不幸运。其它的选择是:

  1. 构建某种自定义的类/元类东西,PEP兼容484型跳棋可以理解和使用。
  2. 修改类型检查你使用某种方式(mypy有一个实验性的“插件”系统,例如)
  3. 信访修改PEP 484,包括新的,自定义类型(您可以通过打开的问题做到这一点typing module repo)。
+0

谢谢,这里使用'TypeVar'就是我所缺少的。非常神奇的是,仿制药“就这样工作”。 – shadowtalker

这正是__getitem__方法,所有的魔法。

这是在您使用[]括号来订阅一个名称时调用的方法。

所以,你需要在你的类的类中有一个__getitem__方法 - 也就是它的元类,它将作为参数在括号内得到。该方法负责动态创建(或检索缓存副本)的任何你想要生成的,并返回它。

我只是不可能想象如何你想要这种类型的暗示,因为打字库似乎涵盖所有合理的情况下(我想不出一个他们没有覆盖的例子)。但是,让你想一类返回其自身的副本,但与参数anotated作为其type_属性的假设:

class MyMeta(type): 
    def __getitem__(cls, key): 
     new_cls = types.new_class(f"{cls.__name__}_{key.__name__}", (cls,), {}, lambda ns: ns.__setitem__("type", key)) 
     return new_cls 

class Base(metaclass=MyMeta): pass 

而在交互模式尝试这一点,可以这样做:

In [27]: Base[int] 
Out[27]: types.Base_int 
+0

谢谢,虽然我还不确定这是如何工作的。你能说一说我的问题吗?你的答案中的例子与我正在做的事情有点不同。 – shadowtalker

+1

@jsbueno - 我不认为你的答案有效。您提出的代码当然是一种构建*看起来像* PEP 484类型的方法,但由于它不是,PEP 484兼容类型检查器不会理解如何处理您的'MyMeta'或'Base'类。 – Michael0x2a