【问题标题】:python Generic Type Hints + user-defined container + constraining to Types implementing __add__ methodpython 通用类型提示 + 用户定义的容器 + 约束到实现 __add__ 方法的类型
【发布时间】:2020-09-30 15:44:02
【问题描述】:

我想在 python 3.8 中实现一个 Liste 类,它具有头尾、car()、cdr() 和 nil 的 Lisp 方式。我想在 Liste 中定义一个接受 Type T 对象的泛型类型。

from __future__ import annotations
from typing import TypeVar, Callable, Generic

T = TypeVar('T')
class Liste(Generic[T]):

    def __init__(self, h: T, t: Liste[T]) -> None:
        self.head = h
        self.tail = t

    @staticmethod
    def nil() -> Liste[T]:
        return Liste(None, None)

    def est_vide(self) -> bool:
        return (self.head is None) and (self.tail is None)
    def cdr(self)->Liste[T]:
        if self.tail is None: return Liste.nil()
        else: return  self.tail

    def sum(self)->T:
        if self.est_vide():
            return 0
        else:
            return self.head + self.cdr().sum()

一直以来,我在类型提示方面度过了一段非常愉快的时光。但是mypy指出了4个错误

liste_sof.py:13: error: Argument 1 to "Liste" has incompatible type "None"; expected "T"
liste_sof.py:13: error: Argument 2 to "Liste" has incompatible type "None"; expected "Liste[T]"
liste_sof.py:23: error: Incompatible return value type (got "int", expected "T")
liste_sof.py:25: error: Unsupported left operand type for + ("T")
Found 4 errors in 1 file (checked 1 source file)

问题 1 是能够指定我期望实现 __add__ 方法的 T 对象。我不知道该怎么做。

问题2是处理我班级的特殊Liste.nil()空对象。

【问题讨论】:

  • 问题3是找到实现__add__方法的类T的Empty对象。对于str,我想要“”,对于int或float,0就可以了
  • 单独的问题应该是单独的问题
  • 对于th,您需要使用Optional,并且您可以使用协议来描述“支持添加”的结构类型。对于支持添加的所有类型,没有干净的方法可以找到“空对象”。因此,您可以只使用受限类型变量而不是通用协议来支持您想要支持的类型。
  • 老实说,这门课会很学术。例如,您对sum 的实现不适用于大型列表。 Python 不是 Lisp,它们是完全不同的语言。在 python 中,您应该使用迭代而不是递归来实现这一点。另外,您不想支持str,因为当用于连接str 对象的线性时间算法在Python 中很简单时,这只会创建一个隐藏的二次时间算法

标签: python type-hinting abstract-data-type generic-type-argument


【解决方案1】:

Mypy 抱怨是因为如果您希望 ht 能够成为 None,则您需要使用 Optional,否则,您暗示 一切 必须是 @987654325 @,这不是通用的。

您可以使用带有Protocol 的结构类型来表达“有__add__”。

最后,没有干净的方法来获取“空对象”。对于内置类型,type(self)() 可能有效,但老实说,我只会强制 API 采用初始值。

from __future__ import annotations
from typing import TypeVar, Callable, Generic, Protocol, Optional


T = TypeVar('T')

class SupportsAdd(Protocol[T]):
    def __add__(self: T, other: T) -> T:
        ...

A = TypeVar('A', bound=SupportsAdd)

class Liste(Generic[A]):
    def __init__(self, h: Optional[A], t: Optional[Liste[A]]) -> None:
        self.head = h
        self.tail = t

    @staticmethod
    def nil() -> Liste[A]:
        return Liste(None, None)

    def est_vide(self) -> bool:
        return (self.head is None) and (self.tail is None)

    def cdr(self)->Liste[A]:
        if self.tail is None: return Liste.nil()
        else: return  self.tail

    def sum(self, init: A) -> A:
        if self.head is None or self.tail is None:
            return init
        else:
            return self.head + self.cdr().sum(init)

正如我在 cmets 中所说,这门课非常学术,您可能不应该实际使用它。这将是低效的。至少,您不应该对sum 使用递归。

【讨论】:

  • 它确实是学术性的,还没有考虑到效率。谢谢你的回答!
  • 我猜你的提议是 class Liste(Generic[A]): 而不是 class Liste(Generic[T]): ?还是我错过了什么?
  • @smeden 哎呀。是的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-03
  • 1970-01-01
  • 2022-08-18
相关资源
最近更新 更多