Python是否有可能指定通用类型具有某些属性和方法
在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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您的代码几乎可以。
typevar
绑定到协议
,您完全按预期使用它没有错。问题在于协议定义中:int
与compobableProto
不兼容,因为它具有def> def __lt __(self,_____,_____的:int:int) - >布尔
。请注意,int
仅与另一个int
相媲美,而您的协议期望更宽的类型,compobableproto
。您可以使用更简单的程序来验证这一点:mypy
将解释,发生了什么,并用注释:我建议您在 Playground 了解这一点不是很明显。
现在情况很清楚。如果仅定义协议仅具有相同类型的协议(读:至少),则错误将消失:
协议中的dunder名称用于明确说明参数名称并不重要(因此调用
obj .__ lt __(其他=其他=)某物)
将不允许进行协议,但对于具有该参数的任何子类均可允许使用该参数> other
)。显然,__ lt __
是这种情况,因为它旨在用于操作员形式。您的理解是错误的,每个级别实现
__ 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 toProtocol
, you use it exactly as expected. The problem is in protocol definition:int
is not compatible withComparableProto
, because it hasdef __lt__(self, __other: int) -> bool
. Notice thatint
is comparable only with anotherint
, while your protocol expects wider type,ComparableProto
. You can verify that with a simpler program:mypy
will explain, what's happening, with notes: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:
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 namedother
). It's obviously the case for__lt__
, because it is intended for use in operator form.Your understanding is wrong, every class that implements
__lt__
with proper signature is subtype ofComparableProto
(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
fromtyping
(or fromcollections.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 returnsint
, 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).