实现协议的 Python 泛型类型

Posted

技术标签:

【中文标题】实现协议的 Python 泛型类型【英文标题】:Python generic type that implements protocol 【发布时间】:2022-01-21 22:13:25 【问题描述】:

对象 A、B ... 具有属性 namespace,我有一个函数可以通过 namespace 属性的特定值集过滤此类对象列表:

T = TypeVar('T')


def filter(seq: list[T], namespace_values: set[str]) -> list[T]:
    # Returns a smaller list containing only the items from
    # `seq` whose `namespace` are in `namespace_values`
    ...

这很好用,但它允许传递不具有属性 namespaceX 类型的对象而不会出现任何检查错误。

然后我创建了一个协议并更改了功能以使用该协议:


class Namespaced(Protocol):
    namespace: str

def filter(seq: list[Namespaced], namespace_values: set[str]) -> list[Namespaced]:
    # Returns a smaller list containing only the items from
    # `seq` whose `namespace` are in `namespace_values`
    ...

现在,如果我通过 X 的列表(这是我想要的),我会收到一个检查错误,但我丢失了泛型:


list_of_a: list[A] = [a1, a2, a3]

output = filter(list_of_a, ['ns1', 'ns2'])

# output is list[Namespaced] instead of list[A]

如何结合泛型和协议,以便我的函数返回类型 T 的列表并检查 seq 的项目是否实现 Namespaced 协议?

我尝试了以下方法,但 T 丢失了。


def filter(seq: list[Namespaced[T]], namespace_values: set[str]) -> list[T]:
    # Returns a smaller list containing only the items from
    # `seq` whose `namespace` are in `namespace_values`
    ...

干杯!

【问题讨论】:

注意,namespace_values: set(str) 不是一个有效的类型注解,你的意思是namespace_values: set[str] @juanpa.arrivillaga 谢谢!我编辑了。 【参考方案1】:

使用以协议为边界的绑定类型变量。考虑以下模块:

(py39) Juans-MacBook-Pro:~ juan$ cat test.py

其中有:

from typing import TypeVar, Protocol
from dataclasses import dataclass

class Namespaced(Protocol):
    namespace: str


T = TypeVar("T", bound="Namespaced")

@dataclass
class Foo:
    namespace: str

@dataclass
class Bar:
    namespace: str
    id: int

def frobnicate(namespaced: list[T]) -> list[T]:
    for x in namespaced:
        print(x.namespace)
    return namespaced

result1 = frobnicate([Foo('foo')])
result2 = frobnicate([Bar('bar', 1)])

reveal_type(result1)
reveal_type(result2)

然后mypy给出:

(py39) Juans-MacBook-Pro:~ juan$ mypy --strict test.py
test.py:27: note: Revealed type is "builtins.list[test.Foo*]"
test.py:28: note: Revealed type is "builtins.list[test.Bar*]"

【讨论】:

以上是关于实现协议的 Python 泛型类型的主要内容,如果未能解决你的问题,请参考以下文章

仅适用于数值类型的泛型类型约束

JAVA中的泛型类是啥东西?

通过泛型类实现输出学生信息

Kotlin泛型 ① ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 )

使用泛型实现栈结构

根据 Class<T> 的类型创建泛型类的实现实例