How can I create my own “parameterized” type in Python (like `Optional[T]`)?

不羁的心 提交于 2020-02-03 08:00:26

问题


I want to create my own parameterized type in Python for use in type hinting:

class MaybeWrapped:
    # magic goes here

T = TypeVar('T')

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

Never mind the contrived example; how can I implement this? I looked at the source for Union and Optional, but it looks like some fairly low-level hackery that I'd like to avoid.

The only suggestion in the documentation comes from an example re-implementation of Mapping[KT,VT] that inherits from Generic. But that example is more about the __getitem__ method than about the class itself.


回答1:


If you're just trying to create generic classes or functions, try taking a look at the documentation on mypy-lang.org about generic types -- it's fairly comprehensive, and more detailed then the standard library typing docs.

If you're trying to implement your specific example, it's worth pointing out that type aliases work with typevars -- you can simply do:

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))

However, if you're trying to construct a generic type with genuinely new semantics, you're very likely out of luck. Your remaining options are to:

  1. Construct some kind of custom class/metaclass thing that PEP 484-compliant type checkers can understand and use that.
  2. Modify the type checker you're using somehow (mypy has an experimental "plugin" system, for example)
  3. Petition to modify PEP 484 to include your new, custom type (you can do this by opening an issue in the typing module repo).



回答2:


It is exactly the __getitem__ method that does all the magic.

That is the method called in when you subscribe one name with [ and ] brackets.

So, you need an __getitem__ method in the class of your class - that is, its metaclass, that will get as parameters whatever is within the brackets. That method is responsible for dynamically creating (or retrieving a cached copy) of whatever you want to generate, and return it.

I just can't possibly imagin how you want this for type hinting, since the typing library seems to cover all reasonable cases (I can't think of an example they don't cover already). But let's suppose you want a class to return a copy of itself, but with the parameter anotated as its type_ attribute:

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

And on trying this in interactive mode, one can do:

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


来源:https://stackoverflow.com/questions/46382170/how-can-i-create-my-own-parameterized-type-in-python-like-optionalt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!