【发布时间】:2020-09-07 00:51:34
【问题描述】:
总结
如何使用类变量的值(它是一个类对象)作为 Python 类型 / mypy 的(mixin)方法的返回类型?
这是一个最小的示例,您将在下面找到更复杂的真实用例:
from typing import Generic, Type, TypeVar
T = TypeVar('T')
class Base(Generic[T]):
return_type: Type[T]
value: T # This attribute is only needed for this minimal example
class Mixin:
def get(self: Base[T]) -> T: # mypy: The erased type of self "Base" is not a supertype of its class "Mixin"
return self.return_type(self.value) # mypy: Too many arguments for "object"
class Concrete(Mixin, Base[int]):
return_type = int
def __init__(self):
self.value = 3
c = Concrete()
x: str = c.get() # mypy: expected incompatible types (str vs int) error :)
将Base 设置为Mixin 的超类时,我可以摆脱第二个错误,但这并不是我真正想要的。不过,我现在知道如何正确定义return_type: Type[T]。
我已经阅读了 python 类型文档以及 mypy 文档,但什么也没找到。网络搜索也没有产生有用的结果。
我真正想做的事
我目前正在编写一个架构类似于python-gitlab的REST客户端:
用户使用知道 API URL 并执行所有 HTTP 请求的
ApiClient类。API 的端点由作为
ApiClient属性的REST 管理器类表示。根据端点的功能,REST 管理器可以列出端点的对象、获取单个对象或创建、更新和删除对象。具体的 REST 管理器子类化
RestManager基类和用于 HTTP 操作的各种 mixin,例如用于通过 ID 获取单个对象的GetMixin。一个具体的 REST 管理器有一个类变量,它保存它要返回的对象的类。
在mixin类中,我想表达“这个方法返回一个对象类的实例,即子类化restmanager定义为类变量”。
示例用法:
client = ApiClient('https://example.com/myapi/v1')
item = client.items.get(42)
assert isinstance(item, Item)
实施:
from typing import ClassVar, Type, TypeVar
T = TypeVar(T)
class Item:
"""Data class that represents objects of the "items" endpoint"""
pass
class ApiClient:
"""Main object that the user works with."""
def __init__(self, url: str):
self.url = url
# There is one manager instance for each endpoint of the API
self.items = ItemManager(self)
# self.cats = CatManager(self)
def http_get(self, path: str) -> 'Response':
... # Request the proper url and return a response object
class RestManager:
"""Base class for REST managers."""
_path: ClassVar[str]
_obj_cls: ClassVar[Type[T]] # Concrete subclasses set this with an object class, e.g., "Item"
def __init__(self, client: ApiClient):
self.client = client
@property
def path(self) -> str:
return self._path
class GetMixin:
"""Mixin for getting a single object by ID"""
def get(self: RestManager, id: int) -> T: # Return type is the value the subclass' "_obj_cls" attribute
response = self.client.http_get(f'{self.path}/{id}')
return self._obj_cls(**response.json())
class ItemsManager(GetMixin, RestManager):
"""Concrete manager for "Item" objects."""
_path = '/items'
_obj_cls = Item # This is the return type of ItemsManager.get()
client = ApiClient()
item = client.items.get(42)
assert isinstance(item, Item)
【问题讨论】:
标签: python type-hinting mypy