【问题标题】:Perform generic functions on a specific typed REST endpoint keeping the types在保留类型的特定类型化 REST 端点上执行通用功能
【发布时间】:2018-11-01 14:52:58
【问题描述】:

我正在制作一个类型化的 REST 库,其中所有端点都有特定的类,并且在对象上设置了它们的方法。假设我们有一个由端点 A 返回的字符串列表,它会在下面有 MVCE 类 A。我在Base 类中添加了所有端点需要运行的方法,以便端点包含尽可能少的样板。

但是,我需要在所有“列表”端点上执行一些功能,例如下面的AB,但不是C。这个常用函数是get_all,这样我们就可以从列表中获取所有的对象了。

问题是我的代码可以工作,但是 PyCharm 和 mypy 不知道 ab 的类型,并说类型是 List[T],这是有道理的,因为我没有指定什么T 是。

如何使a 具有List[str] 类型,而b 具有List[int] 类型?

_mock_a = list('abcdefghijklmnopqrstuvwxyz')
_mock_b = [int(i) for i in '12345678901234567890123456']

from typing import TypeVar, Callable, List

T = TypeVar('T')


class Base:
    def pipe(self, fn: Callable[['Base'], List[T]]) -> List[T]:
        return fn(self)


class A(Base):
    def get(self, index=0, count=5) -> List[str]:
        return _mock_a[index:index+count]

    def count(self) -> int:
        return len(_mock_a)


class B(Base):
    def get(self, index=0, count=5) -> List[int]:
        return _mock_b[index:index+count]

    def count(self) -> int:
        return len(_mock_b)


class C(Base):
    def other(self) -> None:
        pass


def get_all(base: Base) -> List[T]:
    step = 5
    return [
        item
        for start in range(0, base.count(), step)
        for item in base.get(start, step)
    ]


# Has type List[T], but I want it to have List[str]
a = A().pipe(get_all)
print(a)
# Has type List[T], but I want it to have List[int]
b = B().pipe(get_all)
print(b)

我尝试了以下方法来解决这个问题,但都没有成功。

class Method(Generic[T]):
    @staticmethod
    def get_all(base: Base) -> List[T]:
        step = 5
        return [
            item
            for start in range(0, base.count(), step)
            for item in base.get(start, step)
        ]


a = A().pipe(Method[str].get_all)
print(a)
class Base:
    def pipe(self, t: Type[T], fn: Callable[['Base'], T]) -> T:
        return fn(self)


a = A().pipe(List[str], get_all)
print(a)

我找到了让第二个工作的方法,就像typing.cast

class Base:
    def pipe(self, fn: Callable[['GetableEndpoint[T]'], List[T]], t: Type[T]=T) -> List[T]:
        return fn(cast(GetableEndpoint[T], self))


class GetableEndpoint(Generic[T], Base, metaclass=abc.ABCMeta):
    @classmethod
    def __subclasshook__(cls, C):
        if cls is GetableEndpoint:
            if any('get' in B.__dict__ for B in C.__mro__) and any('count' in B.__dict__ for B in C.__mro__):
                return True
        return NotImplemented

    @abc.abstractmethod
    def get(self, index=0, count=5) -> List[T]:
        raise NotImplementedError()

    @abc.abstractmethod
    def count(self) -> int:
        raise NotImplementedError()


def get_all(base: GetableEndpoint[T]) -> List[T]:
    step = 5
    return [
        item
        for start in range(0, base.count(), step)
        for item in base.get(start, step)
    ]


a = A().pipe(get_all, str)

【问题讨论】:

  • 我认为 PyCharm 会在 base.get(start, step) 处退出,因为它无法从 Base 类中推断出返回类型。您是否尝试实现一个空的Base.get 来帮助类型推断?

标签: python python-3.x types pycharm mypy


【解决方案1】:

问题Python type annotation for custom duck type 与此问题类似,并包含指向Protocols (a.k.a. structural subtyping) 的链接。这个问题创建了PEP 544,它有一个implementation in typing_extensions

这意味着要解决上述问题,我们可以将GetableEndpoint 更改为Protocol

from typing import TypeVar, List
from typing_extensions import Protocol
import abc

T = TypeVar('T')


class GetableEndpoint(Protocol[T]):
    @abc.abstractmethod
    def get(self, index=0, count=5) -> List[T]:
        pass

    @abc.abstractmethod
    def count(self) -> int:
        pass

这允许在 PyCharm 和 Mypy 中完全键入:

class A(Base):
    def get(self, index=0, count=5) -> List[str]:
        return _mock_a[index:index+count]

    def count(self) -> int:
        return len(_mock_a)


def get_all(base: GetableEndpoint[T]) -> List[T]:
    step = 5
    return [
        item
        for start in range(0, base.count(), step)
        for item in base.get(start, step)
    ]


a = get_all(A())
print(a)

我无法让pipe 工作,但是现在它具有完全工作的类型,我认为这更重要。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-10
    • 2016-04-19
    • 1970-01-01
    • 2022-01-12
    相关资源
    最近更新 更多