我知道Python不是Haskell或Ocaml,但这是在Python(2或3)中定义代数数据类型的最佳方法吗?谢谢!
Macropy提供代数数据类型,模式匹配等等!
这是以相对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
。
这些和类型可以与产品类型(list
或tuple
)结合使用,并且仍然保留代数数据类型所需的许多关键功能。
问题
这并不严格限制变体集。
在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)