在Python中定义代数数据类型的最佳方法?

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

我知道Python不是Haskell或Ocaml,但这是在Python(2或3)中定义代数数据类型的最佳方法吗?谢谢!

python algebraic-data-types
3个回答
17
投票

Macropy提供代数数据类型,模式匹配等等!


0
投票

这是以相对Pythonic方式实现的和类型。

import attr


@attr.s(frozen=True)
class CombineMode(object):
    kind = attr.ib(type=str)
    params = attr.ib(factory=list)

    def match(self, expected_kind, f):
        if self.kind == expected_kind:
            return f(*self.params)
        else:
            return None

    @classmethod
    def join(cls):
        return cls("join")

    @classmethod
    def select(cls, column: str):
        return cls("select", params=[column])

破解翻译,你会看到熟悉的行为:

>>> CombineMode.join()
CombineMode(kind='join_by_entity', params=[])

>>> CombineMode.select('a') == CombineMode.select('b')
False

>>> CombineMode.select('a') == CombineMode.select('a')
True

>>> CombineMode.select('foo').match('select', print)
foo

注意:@attr.s装饰器来自attrs library,它实现了__init____repr____eq__,但它也冻结了对象。我把它包括在内是因为它减少了实现的大小,但它也广泛可用并且相当稳定。

求和类型有时称为带标记的联合。在这里,我使用kind成员来实现标记。其他每个变量参数通过列表实现。在真正的Pythonic方式中,这是输入和输出方面的鸭子类型,但不是内部严格执行。

我还包括一个match函数,它执行基本的模式匹配。类型安全也通过鸭子类型实现,如果传递的lambda函数签名与您尝试匹配的实际变体不对齐,则会引发TypeError

这些和类型可以与产品类型(listtuple)结合使用,并且仍然保留代数数据类型所需的许多关键功能。

问题

这并不严格限制变体集。


0
投票

在Python中,变量已经可以有多个实例(当然不是同时的)。

>>> x = 5 
>>> type(x)
<type 'int'>
>>> x = ["you", "me", "them"]
>>> type(x)
<type 'list'> 

例如,在您的代码中,您可以:

def f(x):

    if isinstance(x, int):
        pass
    elif isinstance(x, float):
        pass
    else:
        raise TypeError

如果你想更接近Haskell,你可以做这样的事情。在Haskell说你有

data Item = Person String Int String | Car String Bool

在Python 3.6中你写

def g(x):
    tag, *values = x

    if tag == 'Person':
        name, age, e_mail_address = values

        # do something
        pass
    elif tag == 'Car':    
        brand, is_diesel = values

        # do something
        pass
    else:
        raise TypeError

在Haskell中,它也被称为“和类型”。

另一种方法是使用类。发生了什么更明确的事情。例如Haskell的Either

data Either a b = Left a | Right b

在Python Either Int Float中它会是这样的

class Either:

    def __init__(self, a=None, b=None):
        if (a is None) and (b is not None):                 
            self._left  = None
            self._right = float(b) 
        elif (a is not None) and (b is None): 
            self._left  = int(a)
            self._right = None
        else:
            raise TypeError 

    @property
    def is_left(self): 
        return self._left is not None

    @property
    def is_right(self):
        return self._right is not None

    @property 
    def value(self):
        if self.is_left:
            return self._left
        elif self.is_right:
            return self._right 

    def __eq__(self, other):
        if isinstance(other, Either):
            if self.is_left == other.is_left:
                return self.value == other.value 
            else:
                return False   
        else:
            raise TypeError 

    def __str__(self):
        return str(self.value)
© www.soinside.com 2019 - 2024. All rights reserved.