Подтвердить что ты не робот

Лучший способ определить типы алгебраических данных в Python?

Я знаю, что Python не является Haskell или Ocaml, но что является лучшим способом определить типы алгебраических данных в Python (2 или 3)? Спасибо!

4b9b3361

Ответ 1

Macropy предоставляет алгебраические типы данных, сопоставление образцов и многое другое!

Ответ 2

В 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

Если вы хотите остаться ближе к Хаскеллу, вы можете сделать что-то вроде этого. Скажи в Хаскеле

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 Either is

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)

Ответ 3

Здесь реализация типов сумм относительно 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, он реализует __init__, __repr__ и __eq__, но он также замораживает объект. Я включил его, потому что он сокращает размер реализации, но он также широко доступен и довольно стабилен.

Типы сумм иногда называют помеченными объединениями. Здесь я использовал kind член для реализации тега. Дополнительные параметры для каждого варианта реализуются через список. В истинно Pythonic-стиле это типизировано по сторонам на входе и выходе, но не строго соблюдается внутри.

Я также включил функцию match которая выполняет базовое сопоставление с образцом. Безопасность типов также реализована с помощью утилитарной типизации, а TypeError будет вызываться, если переданная сигнатура лямбда-функции не совпадает с фактическим вариантом, которому вы пытаетесь сопоставить.

Эти типы сумм можно комбинировать с типами продуктов (list или tuple) и при этом сохранять большую часть критических функций, необходимых для алгебраических типов данных.

Проблемы

Это не строго ограничивает набор вариантов.