Python是否有可能指定通用类型具有某些属性和方法

发布于 2025-01-25 19:44:44 字数 969 浏览 1 评论 0原文

在Python中,我想创建一个可以接受通用参数并返回相同类型的函数,我想以某种方式指定通用性具有某些属性和方法。我以为将typevar绑定到协议可以完成这项工作,但是我

只是为了举例说明了一个函数,例如,可以说该函数对输入进行分类。某些对象的数组和返回这些对象的排序阵列。对数组元素的唯一限制是,他们必须实现__ lt __

from __future__ import annotations
from typing import List, Protocol, TypeVar

class ComparableProto(Protocol):
    def __lt__(self, other: ComparableProto) -> bool: ...

Comparable = TypeVar("Comparable", bound=ComparableProto)

def sort(arr: List[Comparable]) -> List[Comparable]: ...

if __name__ == "__main__":
    sort([1, 2, 3])  # mypy will say: Value of type variable "Comparable" of "sortarr" cannot be "int"  [type-var]

我知道上面的示例无法正常工作,因为可比类型必须是coparable Proto的亚型,仅仅因为某些类型实现__ lt __不会使其成为Compobable Proto的子类型。

我注意到键入模块实现了几种名为支持*的通用协议类型,但是它们与我想要的不同之处

是否有什么方法可以实现我想要的东西?

In python I would like to create a function that would accept a generic parameter and that would return a value of the same type and I would like to somehow specify that the generic has some attributes and methods. I thought that binding a TypeVar to a Protocol would do the job, but I was terribly wrong

Just to give an example of such a function, let's say the function sorts the input array of some objects and returns sorted array of those objects. The only restriction on the array's elements is that they have to implement __lt__

from __future__ import annotations
from typing import List, Protocol, TypeVar

class ComparableProto(Protocol):
    def __lt__(self, other: ComparableProto) -> bool: ...

Comparable = TypeVar("Comparable", bound=ComparableProto)

def sort(arr: List[Comparable]) -> List[Comparable]: ...

if __name__ == "__main__":
    sort([1, 2, 3])  # mypy will say: Value of type variable "Comparable" of "sortarr" cannot be "int"  [type-var]

I understand that the above example can't work, because Comparable type must be subtype of ComparableProto and just because some type implements __lt__ doesn't make it subtype of ComparableProto.

I noticed that typing module implements several generic protocol types named Supports*, but they differ from what I want

Is there some way to achieve what I want?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

贱贱哒 2025-02-01 19:44:44

您的代码几乎可以。 typevar绑定到协议,您完全按预期使用它没有错。问题在于协议定义中:intcompobableProto不兼容,因为它具有def> def __lt __(self,_____,_____的:int:int) - >布尔。请注意,int仅与另一个int相媲美,而您的协议期望更宽的类型,compobableproto。您可以使用更简单的程序来验证这一点:

from __future__ import annotations
from typing import Protocol

class ComparableProto(Protocol):
    def __lt__(self, other: ComparableProto) -> bool: ...

x: ComparableProto = 1

mypy将解释,发生了什么,并用注释:

main.py:7: error: Incompatible types in assignment (expression has type "int", variable has type "ComparableProto")
main.py:7: note: Following member(s) of "int" have conflicts:
main.py:7: note:     Expected:
main.py:7: note:         def __lt__(self, ComparableProto) -> bool
main.py:7: note:     Got:
main.py:7: note:         def __lt__(self, int) -> bool

我建议您在 Playground 了解这一点不是很明显。

现在情况很清楚。如果仅定义协议仅具有相同类型的协议(读:至少),则错误将消失:

from __future__ import annotations
from typing import List, Protocol, TypeVar

_T = TypeVar('_T')

class ComparableProto(Protocol):
    def __lt__(self: _T, __other: _T) -> bool: ...  # Only same type allowed

Comparable = TypeVar("Comparable", bound=ComparableProto)

def sort(arr: List[Comparable]) -> List[Comparable]:
    return arr

sort([1, 2, 3])

协议中的dunder名称用于明确说明参数名称并不重要(因此调用obj .__ lt __(其他=其他=)某物)将不允许进行协议,但对于具有该参数的任何子类均可允许使用该参数> other)。显然,__ lt __是这种情况,因为它旨在用于操作员形式。

我知道上面的示例无法正常工作,因为可比类型必须是可比性proto的亚型,仅仅是因为某些类型实现__ lt __ lt __不会使其成为compobable Proto的子类型。

您的理解是错误的,每个级别实现__ lt __具有适当签名 的子类型 compobableproto (和“子类”术语对于协议并不那么重要, )。协议用于表示结构性亚型,因此另一类不需要从协议中继承就可以与之兼容。例如,大小来自键入(或collections.abc(如果您足够现代并使用python 3.9+)是一个定义的协议__ len __(self) - > int。返回int的实现__ len __的任何类都与(读:subtype of)sip sized sized ,尽管不继承。参见 pep544 有关更多正式规则(请注意,请注意使用“亚型”术语而不是“子类”来避免误解)。

Your code is almost fine. There's nothing wrong with TypeVar bound to Protocol, you use it exactly as expected. The problem is in protocol definition: int is not compatible with ComparableProto, because it has def __lt__(self, __other: int) -> bool. Notice that int is comparable only with another int, while your protocol expects wider type, ComparableProto. You can verify that with a simpler program:

from __future__ import annotations
from typing import Protocol

class ComparableProto(Protocol):
    def __lt__(self, other: ComparableProto) -> bool: ...

x: ComparableProto = 1

mypy will explain, what's happening, with notes:

main.py:7: error: Incompatible types in assignment (expression has type "int", variable has type "ComparableProto")
main.py:7: note: Following member(s) of "int" have conflicts:
main.py:7: note:     Expected:
main.py:7: note:         def __lt__(self, ComparableProto) -> bool
main.py:7: note:     Got:
main.py:7: note:         def __lt__(self, int) -> bool

I suggest you to play a little with this code in playground to understand this not very obvious part better.

Now things are clear. If you define your protocol comparable only (read: at least) with same type, error will go away:

from __future__ import annotations
from typing import List, Protocol, TypeVar

_T = TypeVar('_T')

class ComparableProto(Protocol):
    def __lt__(self: _T, __other: _T) -> bool: ...  # Only same type allowed

Comparable = TypeVar("Comparable", bound=ComparableProto)

def sort(arr: List[Comparable]) -> List[Comparable]:
    return arr

sort([1, 2, 3])

Dunder name in protocols is used to explicitly say that name of argument is not important (so calling obj.__lt__(other=something) will be disallowed for protocol, but may be allowed for any subclass that has this argument named other). It's obviously the case for __lt__, because it is intended for use in operator form.

I understand that the above example can't work, because Comparable type must be subtype of ComparableProto and just because some type implements __lt__ doesn't make it subtype of ComparableProto.

Your understanding is wrong, every class that implements __lt__ with proper signature is subtype of ComparableProto (and "subclass" term is not so important for protocols). Protocols are used to represent structural subtyping, so another class doesn't need to inherit from protocol to be compatible with it. For instance, Sized from typing (or from collections.abc if you're modern enough and use python 3.9+) is a protocol that defines __len__(self) -> int. Any class that implements __len__, that returns int, is compatible with (read: subtype of) Sized despite not inheriting from it. See PEP544 for more formal rules (note that "subtype" term is used instead of "subclass" to avoid misunderstanding).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文