【问题标题】:User defined generic types and collections.abc用户定义的泛型类型和 collections.abc
【发布时间】:2016-01-12 04:25:46
【问题描述】:

我有一个 Python 包,它基于 collections.abc 提供的 ABC(MappingSequence 等)定义了各种集合)。我想利用 Python 3.5 中引入的类型提示功能,但我怀疑最好的方法是什么。

让我们以其中一个类为例;直到现在,我有一些东西 类似这样:

from collections.abc import Mapping

class MyMapping(Mapping):
    ...

要将其转换为泛型类型,documentation 建议执行以下操作:

from typing import TypeVar, Hashable, Mapping

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class MyMapping(Mapping[K, V]):
    ...

但这带来了两个问题:

  • 该类丢失了 collections.abc.Mapping 中的所有 mixin 方法。我可以自己实现它们,但这会破坏首先使用 ABC 的部分目的。

  • isinstance(MyMapping(), collections.abc.Mapping) 返回 False。此外,尝试调用 collections.abc.Mapping.register(MyMapping) 来解决此问题会引发 RuntimeError(“拒绝创建继承循环”)。

我第一次尝试解决这些问题是回到扩展collections.abc.Mapping

from typing import TypeVar, Hashable
from collections.abc import Mapping

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class MyMapping(Mapping[K, V]):
    ...

但这不起作用,因为 collections.abc.Mapping 不是泛型类型并且不支持订阅运算符。所以我尝试了这个:

from typing import TypeVar, Hashable, Mapping
from collections.abc import Mapping as MappingABC

K = TypeVar("K", bound=Hashable)
V = TypeVar("V")

class MyMapping(MappingABC, Mapping[K, V]):
    ...

但这闻起来很腥。导入和别名比较麻烦,生成的类有曲折的MRO,ABC提供的mix-in方法不会有打字信息……

那么,基于集合 ABC 声明自定义泛型类型的首选方式是什么?

【问题讨论】:

    标签: python python-3.x generics type-hinting


    【解决方案1】:

    [ Personal Opinion™ ]:我真的不支持创建新的typing 功能。这些应该足够通用,不需要对您的代码进行任何修改。如果您的映射类非常复杂,它不能被任何常见的映射(如dict)取代,那么您最好只使用它自己:

    def foo(bar: MyMapping) -> List:
        pass
    

    而不是

    def foo(bar: Mapping[K, V]) -> List:
        pass
    

    现在,如果您希望您的用户能够使用typing.Mapping“输入”检查您的类,您只需将collections.Mapping 子类化

    class MyMapping(collections.abc.Mapping):
        ... # define required methods
    
    isinstance(MyMapping(), typing.Mapping[K, V]) # --> True
    

    【讨论】:

    • [我的个人意见]:我自己并不擅长静态类型或类型提示,但我理解对某些人的吸引力以及它可以为 IDE 和其他工具带来的好处。我喜欢它是 API 编写者和用户可以选择加入的可选层。
    • 我希望我的 API 的用户能够表明他们的一个函数返回一个 MyMapping[str, int] 类型的对象,如果他们愿意的话。如果他们只想声明它返回 MyMapping、或 Mapping[str, int] 或 Mapping……这些也都很好。如果他们想完全跳过类型提示,那么对他们来说有更多的权力。这应该是他们的选择。就目前而言,我可以说“这个函数返回一个 MyMapping 的实例”或“这个函数返回一个带有字符串键和整数值的映射”,但我不能混合使用这两个语句。这似乎是一个奇怪的限制。
    • @MartiCongost 我的意思是:您的用户不应该使用您的类进行静态类型...如果您的类是一个映射,他们应该只使用typing.Mapping[K, V]...否则,他们无法将dict 传递给所述函数
    • 我同意在函数参数上使用更多通用类型比将用户限制在特定选择上更有意义。但是还有其他用例,例如属性和函数返回值,在这些用例中,指定您正在处理的精确类型是有利的。
    • (就像打字模块的文档建议使用 MappingSequence 作为函数参数,并且只使用 Dict i> 和 List 表示返回值)。
    【解决方案2】:

    您的原始代码在当前版本的 python 和 mypy 上运行良好,并且完全按照您的意愿执行所有操作(包括重用来自 collections.abc.Mapping 的实现)。

    但是,暂时你应该删除bound=Hashable,因为它是not fully supported yet

    from typing import TypeVar, Hashable, Mapping
    
    K = TypeVar("K")
    V = TypeVar("V")
    
    class MyMapping(Mapping[K, V]):
        ...
    

    【讨论】:

    • 感谢您的提醒。已经有一段时间了,我目前没有在做这个,但我会尝试一下,看看我是否可以接受你的回答。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-21
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    相关资源
    最近更新 更多