Python类型:使用类变量的值作为(mixin)方法的返回类型

2024-04-26 04:19:59 发布

您现在位置:Python中文网/ 问答频道 /正文

总结

如何使用类变量的值(它是一个类对象)作为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文档,但什么也没找到。网络搜索也没有产生有用的结果

我真正想做的是什么

我目前正在编写一个REST客户端,其架构类似于python-gitlab

  • 用户使用一个ApiClient类,该类知道API URL并执行所有HTTP请求

  • API的端点由REST管理器类表示,这些类是ApiClient的属性。根据端点的功能,REST管理器可以列出端点的对象、获取单个对象或创建、更新和删除对象

  • RestManager返回并接收“哑”数据类(例如attrspydantic模型)

  • 具体的REST管理器子类aRestManager基类和各种混合用于HTTP操作,例如,aGetMixin用于按ID获取单个对象。

  • 具体的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)

Tags: the对象selfclientrestforbaseget
1条回答
网友
1楼 · 发布于 2024-04-26 04:19:59

免责声明:我没有仔细阅读您的实际用例,所以我可能是错的。以下分析基于您的简化示例

我认为mypy不支持这一点。当前mypy假定(并且理所当然地)方法中的self类型是类的子类型

但是,为了使mixin正常工作,必须对可以混合的类的类型进行限制。例如,在简化的示例中,类必须具有return_typevalue属性。我的建议是,您还可以将这些作为注释添加到yout mixin类中,结果如下:

T = TypeVar('T')


class Base(Generic[T]):
    return_type: Type[T]
    value: T  # This attribute is only needed for this minimal example


class Mixin(Generic[T]):
    return_type: Type[T]
    value: T

    def get(self) -> T:  # annotation on `self` can be dropped
        return self.return_type(self.value)

请注意,mypy中的最后一行仍然是一个错误,因为它无法证明self.return_type__init__方法只接受一个参数。您可以使用Python3.7中引入的^{}解决这个问题,但这可能有点过分。嗯,如果你有一些简单的代码,你肯定是对的,有时候一点# type: ignore对你来说是最好的

相关问题 更多 >