gpt4 book ai didi

python - 在保持类型的特定类型 REST 端点上执行通用函数

转载 作者:太空宇宙 更新时间:2023-11-03 11:39:16 24 4
gpt4 key购买 nike

我正在制作一个类型化的 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)

最佳答案

问题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 工作,但是现在它有完全可用的类型,我认为这更重要。

关于python - 在保持类型的特定类型 REST 端点上执行通用函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53103750/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com