Python typing: return type with generics like Clazz[T] as in Java Clazz<T>

我只是一个虾纸丫 提交于 2020-06-27 16:53:32

问题


So I am aware of pythons typing.Optional. But I wrote my own crude PyOptional (https://github.com/felixhertrampf/PyOptional/blob/master/PyOptional.py) and would like to combine Optional[T] with my PyOptional to PyOptional[T].

I am currently using Python 3.7 and tried extending typing.Optional.

Some of my PyOptional

class PyOptional:
    T: TypeVar = TypeVar("T")

    def __init__(self, obj: T):
        self.value: Any = obj

    def get(self) -> Optional[T]:
        return self.value

    def or_else(self, default) -> T:
        return self.value or default

Pseudo code of what I want:

def find_user_by_id(id: int) -> PyOptional[User]:
    return PyOptional(db.find_user_by_id(id))

The goal is for my IDE to be able to check what return type to expect and still be able to invoke my methods on the returned object. So it would have to be PEP compliant.


回答1:


You should review the documentation on generics -- specifically, user-defined generics. The mypy docs also have a thorough overview of generics that can be useful to reference.

In this particular case, you want to make the entire class generic by adding in a Generic[T] as a class base. Just using T in the individual function signatures will make each individual function generic, but not the class as a whole:

from typing import TypeVar, Generic, Optional

T = TypeVar("T")

class PyOptional(Generic[T]):
    def __init__(self, obj: Optional[T]) -> None:
        self.value = obj

    def get(self) -> Optional[T]:
        return self.value

    def or_else(self, default: T) -> T:
        return self.value or default

Some additional notes:

  1. Don't add an annotation for any TypeVar variable. Here, T is a sort of meta-type construct that serves as "hole"/can represent any number of types. So, assigning it a fixed type doesn't really make sense, and will confuse type checkers.

  2. Never use a TypeVar only once in any given signature -- the whole point of using TypeVars is so that you can declare two or more types are always going to be the same.

    Note that the fixed PyOptional class above also obeys this rule. For example, take get. Now that we made the whole class generic, the type signature for this function is now basically something like def get(self: PyOptional[T]) -> Optional[T]. Before, it was more like def get(self: PyOptional) -> Optional[T].

  3. For your class to make sense, you probably want your constructor to accept an Optional[T] instead of just T.

  4. Making self.value Any is probably unnecessary/is unnecessarily too vague. We can leave off the type hint, and now it'll have an inferred type of Optional[T].

  5. If you want to more thoroughly check whether or not your class is PEP 484 compliant and will likely be understood by IDEs such as PyCharm, consider type-checking your class + some code using your class via mypy, a PEP 484 type checker.

    This won't guarantee that your IDE will fully understand your class (since it might not fully implement everything about PEP 484/you might run into a bug in either mypy or your IDE), but it should help you get pretty close.



来源:https://stackoverflow.com/questions/57867124/python-typing-return-type-with-generics-like-clazzt-as-in-java-clazzt

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