piątek, 19 września 2014

Python: parsowanie enum ze stringa

Po programowaniu w C# czy Java ciężko mi wyobrazić sobie życie bez typu Enum. Cierpię nie raz bez niego w PHP. Aktualnie ucząc się języka Python uszczęśliwiło mnie odkrycie, że w Python 3.4 został dodany typ Enum. Jeden z podstawowych scenariuszy dla mnie to parsowanie wartości enum z ciągu znaków.

Aktualnie jestem na etapie odkrywania języka Python, ale widziałem już opinie, że Enum jest zbędny, a nawet niezgodny z jego filozofią. Nie wiem, nie znam się, nie wypowiadam się o tym. Chętnie poznam sensowne argumenty w tym temacie, ale nie zamierzam się w nim zagłębiać. Mój cel to przedstawienie mojego pomysłu na parsowanie ciągów znaków do wartości Enum.

Enum w Python jest bazowo bliższy rozwiązaniu z C#. Widziałem już rozszerzenia zbliżające go znacznie do rozwiazania w języku Java. Przy moich zastsowaniach rozwiązanie bazowe jest zwykle wystarczające. W miejscach gdzie nie jest, klasa OrderedEnum czy IntEnum opisane w dokumentacji Python 3.4. Od razu ostrzegam, że to przykłady rozszerzenia Enum, a nie klasy dostarczone z Python 3.4 w module enum. Moje rozwiązanie należy traktować jako kolejny przykład możliwości rozszerzenia klasy Enum. Docelowo zamierzam złożyć klasy OrderedEnum i swoją (aktualnie zwaną jako ExEnum).

Poniżej znajduje się listing mojego aktualnego rozwiązania.
from enum import Enum

class ExEnum(Enum):
    @classmethod
    def parse(cls, name):
        if name in cls.__members__:
            return cls[name]
        raise ValueError("{0} is not valid value of {1}".format(name, cls.__name__))

    @classmethod
    def try_parse(cls, name):
        if name in cls.__members__:
            return cls[name]
        return False
Niestety nie jest możliwe aby oznaczyć klasę ExEnum jako abstrakcyjną. Sama w sobie jest bezużyteczna i służy jako baza, więc powinna być abstrakcyjna, ale nie da się i już. Aby więc skorzystać z ExEnum należy utworzyć klasę z niej dziedziczącą. Można użyć jej jako dowolnego Enuma. Mamy w tym momencie do dyspozycji metody parse i tryparse pozwalające na uzyskanie enuma z ciągu znaków zawierającego jego nazwę.

Przykładowo możemy utworzyć klasę StateEnum jak poniżej.
class StateEnum(ExEnum):
    starting = 0
    started = 1
    working = 2
    stoped = 3
Teraz możemy z niej skorzystać w taki sposób:
>>> StateEnum.parse('started')
<stateenum .started:="" 1="">
>>> StateEnum.parse('paused')
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
    StateEnum.parse('paused')
  File "C:/Python34/tst.py", line 8, in parse
    raise ValueError("{0} is not valid value of {1}".format(name, cls.__name__))
ValueError: paused is not valid value of StateEnum
>>> StateEnum.try_parse('paused')
False
Jak widać metoda parse w razie nieistniejącego elementu rzuca wyjątek. Metoda try_parse zwraca w tej sytuacji False. Jest to podyktowane faktem, że wyjątki nie są zbyt lekkie i można je w tym miejscu uznać za zbędne.

Brak komentarzy:

Prześlij komentarz