Представление сложных объектов в перечислении Python - PullRequest
0 голосов
/ 18 мая 2018

Я работаю с графической структурой, которая состоит из нескольких типов узлов и отношений.Каждый из этих типов имеет некоторые мета-свойства, к которым я хотел бы иметь легкий доступ.Чтобы представить эти типы, я подумал, что перечисление будет лучшим способом.

Я все еще относительно новичок в Python, но я знаю перечисления из других языков, таких как Java.Я думаю, этот вопрос действительно сводится к тому, является ли это хорошим использованием перечислений в Python.Я представляю свои типы следующим образом:

class Grammar(Enum):
    VERB = 'verb'
    NOUN = 'noun'
# END Grammar

class _Word():
    def __init__(self, name, meta):
        self.name = name
        self.meta = meta
    # END __init__
# END _Word

class Word(Enum):
    TYPE_A = _Word('foo', Grammar.VERB)
    TYPE_B = _Word('bar', Grammar.NOUN)
# END Word

Таким образом, каждому из моих значений Word присваивается объект _Word, который является сложным типом.Это прекрасно работает в большинстве случаев, когда используется Enum.Однако мои коллеги отметили, что Spyder генерирует исключение при проверке объекта, в котором присутствует экземпляр перечисления: ValueError(): is not a valid Word (обратите внимание на пробел после двоеточия).

Это заставляет меня думать, что путьЯ использую перечисление не лучшая практика.Я что-то упустил?

1 Ответ

0 голосов
/ 29 мая 2018

Проблема, похоже, заключалась в том, что нескольким библиотекам, таким как pandas и json, трудно сериализовать объекты enum.В Интернете я нашел несколько решений, объясняющих, как написать собственный сериализатор.Однако, поскольку я не могу сказать Spyder или Pandas использовать этот сериализатор, мне нужно что-то другое.

Поработав немного больше, я нашел два подхода, которые решили мою проблему.К сожалению, решение с использованием Enums не работает с Python 2.7, что является обязательным требованием для моего проекта.Таким образом, я объясню и то, и другое.

Python 3.4:

Я добавил типы данных своих значений в качестве миксина для моего класса Enum и аннотировал назначение моего enumзначения как предложено здесь .Специально для использования с фреймами данных мне также нужно было сделать объекты enum сопоставимыми, что я и сделал, определив методы сравнения.Наконец, передайте значения Enum в конструктор для моих сложных объектов.Это устраняет необходимость доступа к свойствам через атрибут value.

class Grammar(str, Enum):
    VERB: str = 'verb'
    NOUN: str = 'noun'

    def __eq__(self, other):
        return not self < other and not other < self
    # END __eq__

    def __ne__(self, other):
        return self < other or other < self
    # END __ne__

    def __gt__(self, other):
        return other < self
    # END __gt__

    def __ge__(self, other):
        return not self < other
    # END __ge__

    def __le__(self, other):
        return not other < self
    # END __le__

    def __lt__(self, other):
        return self.value < other.value
    # END __lt__

    def __hash__(self):
        return hash(self.value)
    # END __hash__
# END Grammar

class _Word(dict):
    def __init__(self, name, meta):
        self['name'] = name
        self['meta'] = meta
    # END __init__
# END _Word

class Word(dict, Enum):
    TYPE_A: dict = _Word('foo', Grammar.VERB)
    TYPE_B: dict = _Word('bar', Grammar.NOUN)

    def __init__(self, _word):
        self['name'] = _word['name']
        self['meta'] = _['meta']
    # END __init__

    def __eq__(self, other):
        return not self < other and not other < self
    # END __eq__

    def __ne__(self, other):
        return self < other or other < self
    # END __ne__

    def __gt__(self, other):
        return other < self
    # END __gt__

    def __ge__(self, other):
        return not self < other
    # END __ge__

    def __le__(self, other):
        return not other < self
    # END __le__

    def __lt__(self, other):
        return self['name'] < other['name']
    # END __lt__

    def __hash__(self):
        return hash(self['name'])
    # END __hash__
# END Word

Это позволило Spyder обрабатывать мое перечисление без каких-либо проблем.В качестве бонуса, при использовании объекта с фреймом данных или при сериализации как json, представления общего объекта теперь заменяются фактическими значениями.Это очень упрощает жизнь!

Python 2.7: К сожалению, реализация Enum для Python 2.7 не поддерживает весь синтаксис, необходимый для работы моего первого решения.В частности, аннотированное назначение VERB: str = 'verb' не допускается.Я закончил тем, что прекратил использование Enums и переключился на атрибуты класса.Это по-прежнему позволяет получить доступ таким образом, чтобы было ясно, что вы имеете дело с константой (например, Grammar.VERB), но это означает, что вы теряете приятные возможности Enum.

Самым важным для меня было то, чтобы я мог выполнять итерацию по моим значениям перечисления, поэтому я создал функцию, которая позволяет мне извлекать все значения из моего псевдо-перечисления:

class PseudoEnum():

    @classmethod
    def getAll(cls):

        """
        Returns a list of tuples representing all entries for this PseudoEnum along the dimensions key x value. This is useful when you need to iterate over the enum.

        :returns: A list of all PseudoEnum entries
        :rtype: list of tuple
        """

        return [(i, getattr(cls, i)) for i in dir(cls) if not i.startswith('__') and not callable((getattr(cls, i)))]
    # END getAll        
# END PseudoEnum

Grammar и Word теперь наследуются от PseudoEnum.Кроме того, методы сравнения изменились с Word до _Word.Grammar больше не нуждается в методах сравнения, так как он имеет дело с обычными строками.

Извиняюсь за длинный ответ.Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...