Python 中的递归数据类型

问题描述 投票:0回答:3

Python 中最接近 Haskell 中的递归数据类型的是什么? (即在定义自身时使用类型自己的定义。)

编辑:

为了给出递归类型的更具体的定义,下面是 Haskell 中的二叉树:

data Tree a             = Leaf a | Branch (Tree a) (Tree a) 

我的阅读方式如下:二叉树可以是叶子,也可以包含两个子树,这两个子树又是类型树本身。

有关Haskell中递归类型的更多信息,您可以参考这里:https://www.haskell.org/tutorial/goodies.html

我实际上想到的是将 Haskell 中的单词树定义转换为 Python。这是我的一个旧项目中对

WordTree
的定义:

data WordTree = Word String | Subword String [WordTree] | Root [WordTree]

A

WordTree
是一个 n 叉树结构,其中单词的公共前缀存储在父节点中,其余部分以排序的方式存储在树的叶子节点中。我相信这种类型定义有点类似于 Trie。然而,由于 Haskell 是一种函数式编程语言,它允许这种类型定义是递归的。 Python 中(或者一般来说,在面向对象的编程中)对于这种类型的定义最接近的可能是什么?

python haskell type-hinting algebraic-data-types recursive-datastructures
3个回答
5
投票

由于 Python 是动态类型的,因此定义您需要的任何类都没有问题。

class Tree:
    left = None
    right = None
    def __init__(self, left, right):
        self.left = left
        self.right = right

即使您有兴趣输入这些定义,您也可以像使用任何其他基于类的面向对象语言一样进行操作:

from typing import Union

class Tree:
    left: Union['Tree', int]
    right: Union['Tree', int]
    def __init__(self, left: Union['Tree', int], right: Union['Tree', int]) -> None:
        self.left = left
        self.right = right

请注意使用字符串作为类型名称(在较新的 Python 版本中可以避免使用字符串)。

请参阅 mypy 中的此开放问题,了解直接递归代数类型,例如

Tree = Union[Tuple['Tree', 'Tree'], int]

定义您描述的

WordTree
的最常见(尽管不一定推荐)的方法是使用超类和浅层层次结构:

from typing import List, final

class WordTree: pass

@final
class Word(WordTree):
    word: str

@final
class Subword(WordTree):
    subword: str
    children: List[WordTree]

@final
class Root(WordTree):
    children: List[WordTree]

使用这样的实现可能需要使用

isinstance
检查(尽管 Python3.10 为您提供了 nice sweet)。本例中省略了构造函数以避免混乱;您可能想使用
dataclass
轻松获取它们以及其他类型的行为。

迄今为止,Python 还没有办法禁止不相关的类从

WordTree
继承,从而破坏了静态推理此类程序的一些能力。

其他一些 OOP 语言,例如 Scala 和 Kotlin 以及(很快)Java,可以采用这样的定义(使用

sealed
)并为您提供与函数式给出的类型检查和语法结构类似的类型检查和语法结构。语言,例如 Haskell。


据我所知,这种设计通常只推荐用于纯数据类,例如 AST。它不太适合定义面向用户的容器(例如 trie),因为它公开了数据结构的内部工作原理。因此,即使您采用该设计,您也可能希望将其用作实现细节,并使用另一个类

Trie
,以便客户端代码通过定义良好的 API 使用。该类可以有一个
WordTree
字段,或者实现相同逻辑的任何其他方式。

在我看来,这对于面向对象设计与功能设计的区别至关重要。后者侧重于数据流和静态推理,而前者侧重于 API、可扩展性和解耦。我认为在语言和环境之间进行移植时,注意这一点很有帮助 - 尽管如上所述,某些语言尝试启用这两种设计方法。


2
投票

这是 Python 3.10 中 Haskell 二叉树的等效实现。静态类型检查可以使用 mypy 完成。

from __future__ import annotations
from dataclasses import dataclass
from typing import Generic, TypeVar

T = TypeVar("T")

@dataclass
class Branch(Generic[T]):
    value: T
    left: Tree[T]
    right: Tree[T]

@dataclass
class Leaf(Generic[T]):
    value: T

Tree = Branch[T] | Leaf [T]

你可以像这样使用它(注意

contains
函数中的模式匹配 - Python 3.10 的新功能):

def contains(tree: Tree[T], value: T):
    match tree:
        case Leaf(x):
            return x == value
        case Branch(x, left, right):
            return x == value or contains(left, value) or contains(right, value)

tree = Branch(
    1,
    Branch(2, Leaf(3), Leaf(4)),
    Branch(5, Leaf(6), Branch(4, Leaf(7), Leaf(8)))
)

assert contains(tree, 1)
assert contains(tree, 5)
assert contains(tree, 8)

要实现您的 WordTree,您需要执行以下操作:

from __future__ import annotations
from dataclasses import dataclass

@dataclass
class Word:
    value: str

@dataclass
class Subword:
    value: str
    trees: list[WordTree]

@dataclass
class Root:
    trees: list[WordTree]

WordTree = Word | Subword | Root

进口注意事项:

  • from __future__ import annotations
    允许您使用尚未定义的类型名称进行注释。
  • @dataclass
    自动为您定义一个构造函数。

0
投票

我看到这篇文章希望实现以下内容:

...整数的嵌套列表

nestedList
。每个元素要么是整数,要么是列表,其元素也可以是整数或其他列表。

我的实现如下。

class NestedInteger(ABC):
    @staticmethod
    # "List" is invariant. https://mypy.readthedocs.io/en/stable/common_issues.html#variance
    def create(data: Sequence[int | list[int]]) -> list[NestedInteger]:
        root: list[NestedInteger] = []
        for x in data:
            match x:
                case int():
                    root.append(NestedInt(x))
                case _:
                    root.append(NestedList(NestedInteger.create(x)))
        return root

    @abc.abstractmethod
    def is_integer(self) -> bool:
        pass

    @abc.abstractmethod
    def get_integer(self) -> int | None:
        pass

    @abc.abstractmethod
    def get_list(self) -> list[NestedInteger] | None:
        pass


@final
@dataclasses.dataclass
class NestedInt(NestedInteger):
    val: int

    def is_integer(self) -> bool:
        return True

    def get_integer(self) -> int | None:
        return self.val

    def get_list(self) -> list[NestedInteger] | None:
        return None


@final
@dataclasses.dataclass
class NestedList(NestedInteger):
    values: list[NestedInteger]

    def is_integer(self) -> bool:
        return False

    def get_integer(self) -> int | None:
        return None

    def get_list(self) -> list[NestedInteger] | None:
        return self.values
    

希望有帮助。

© www.soinside.com 2019 - 2024. All rights reserved.