PythonはHaskellでもOcamlでもないことは知っていますが、Python(2または3)で代数的データ型を定義する最良の方法はどれですか?ありがとう!
Macropy 代数的データ型、パターンマッチングなどを提供します!
In 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
In 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)
これは、比較的Python的な方法での合計型の実装です。
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ライブラリ から取得され、__init__
、__repr__
、および__eq__
を実装しますが、オブジェクトもフリーズします。実装サイズを削減するために含めましたが、広く入手可能で非常に安定しています。
合計タイプは、タグ付き共用体と呼ばれることもあります。ここでは、kind
メンバーを使用してタグを実装しました。追加のバリアントごとのパラメーターは、リストを介して実装されます。真のPythonの方法では、これは入力側と出力側でダックタイピングされますが、内部で厳密に強制されるわけではありません。
基本的なパターンマッチングを行うmatch
関数も含めました。型安全性はダックタイピングによっても実装されます。渡されたラムダの関数シグネチャが、照合しようとしている実際のバリアントと一致しない場合、TypeError
が発生します。
これらの合計タイプは、製品タイプ(list
またはTuple
)と組み合わせることができ、代数的データ型に必要な多くの重要な機能を保持します。
問題
これは、バリアントのセットを厳密に制約するものではありません。