Metadata-Version: 2.1
Name: fast-enum
Version: 1.3.0
Summary: A fast pure-python implementation of Enum
Home-page: https://github.com/QratorLabs/fastenum
Author: Andrey Semenov
Author-email: gatekeeper.mail@gmail.com
License: MIT
Description: # fast_enum
        A fast Enum implementation for Python
        
        The purpose is to replace Python3.4+ standard library Enum.
        
        The main objective to using standard library's Enum is that it's super slow.
        
        Features implemented:
        - as in stdlib's enums all Enum members are instances of the Enum itself
        ```python
        type(LightEnum.ONE)
        # <class 'LightEnum'>
        ```
        - all enum members have at least `name` and `value` properties; the `name` property
         is a string containing the class member's name itself; the `value` property contains the value
          assigned
         ```python
        class ValuesGivenEnum(metaclass=FastEnum):
            ONE: 'ValuesGivenEnum' = 1
            FOUR: 'ValuesGivenEnum' = 4
            ELEVEN: 'ValuesGivenEnum' = 11
        
        ValuesGivenEnum.FOUR
        # <ValuesGivenEnum.FOUR: 4>
        ValuesGivenEnum.FOUR.value
        # 4
        ValuesGivenEnum.FOUR.name
        # 'FOUR'
        ```
        - a lightweight form of enum declaration is possible
        ```python
        class LightEnum(metaclass=FastEnum):
            ONE: 'LightEnum'
            TWO: 'LightEnum'
            THREE: 'LightEnum'
        ```
         When this form is used it is strictly required that members are "type-hinted"
         as instances of the enum class. Otherwise they will remain just as property/attributes
         annotations deep inside the `cls.__annotations__`
        
        - an enum could be accessed by value
        ```python
        LightEnum(1)
        # <LightEnum.ONE: 1>
        ```
        - or by name
        ```python
        LightEnum['ONE']
        # <LightEnum.ONE: 1>
        ```
        
        - it is possible to mix lightweight declaration and a value-provided one in the same class:
        ```python
        class MixedEnum(metaclass=FastEnum):
            _ZERO_VALUED = 1
            AUTO_ZERO: 'MixedEnum'
            ONE: 'MixedEnum' = 1
            AUTO_ONE: 'MixedEnum'
            TWO: 'MixedEnum' = 2
        
        MixedEnum(1)
        # <MixedEnum.ONE: 1>
        MixedEnum.AUTO_ZERO
        # <MixedEnum.AUTO_ZERO: 0>
        MixedEnum.AUTO_ONE
        # <MixedEnum.ONE: 1>
        ```
         When this form is used, if there are more than one Enum with the same value as a result (`MixedEnum.AUTO_ONE.value`
         and `MixedEnum.ONE.value` in this example) all subsequent enums are rendered as just aliases to the first declared
         (the order of declaration is: first value-provided enums then lightweight forms so auto-valued will always become
         aliases, not vice versa). The auto-valued enums value provider is independent from value-provided ones.
        
        - as shown in the previous example, a special attribute `_ZERO_VALUED` could be provided in class declaration;
         given it's value renders to `True` in boolean context auto-valued enums will start from zero instead of integer 1;
         The `_ZERO_VALUED` attribute is erased from the resulting enum type 
        
        - an enum creation can be hooked with 'late-init'. If a special method `def __init_late(self): ...` is provided within
         enum class' declaration, it's run for every enum instance created after all of them are created successfully
        ```python
        class HookedEnum(metaclass=FastEnum):
            halved_value: 'HookedEnum'
        
            __slots__ = ('halved_value',)
        
            def __init_late(self):
                # noinspection PyTypeChecker
                self.halved_value: 'HookedEnum' = self.__class__(self.value // 2)
        
            ZERO: 'HookedEnum' = 0
            ONE: 'HookedEnum' = 1
            TWO: 'HookedEnum' = 2
            THREE: 'HookedEnum' = 3
        
        HookedEnum.ZERO.halved_value
        #<HookedEnum.ZERO: 0>
        HookedEnum.ONE.halved_value
        #<HookedEnum.ZERO: 0>
        HookedEnum.TWO.halved_value
        #<HookedEnum.ONE: 1>
        HookedEnum.THREE.halved_value
        #<HookedEnum.ONE: 1>
        ```
        
        - enums are singletons
        ```python
        from pickle import dumps, loads
        o = LightEnum.ONE
        r = loads(dumps(o))
        id(o)
        # 139649196736456
        id(r)
        # 139649196736456
        o is r
        # True
        ```
        - enums are hashable
        ```python
        list(LightEnum)
        # [<LightEnum.ONE: 1>, <LightEnum.TWO: 2>, <LightEnum.THREE: 3>]
        set(LightEnum)
        # {<LightEnum.ONE: 1>, <LightEnum.TWO: 2>, <LightEnum.THREE: 3>}
        ```
        - enums are easily extended if one needs
        ```python
        class ExtendedEnum(metaclass=FastEnum):
            description: Text
            __slots__ = ('description',)
        
            def __init__(self, value, description, name):
                self.value = value
                self.name = name
                self.description = description
        
            def describe(self):
                return self.description
        
            RED = 'red', 'a color of blood'
            GREEN = 'green', 'a color of grass in the spring'
        
        ExtendedEnum.GREEN
        # <ExtendedEnum.GREEN: green>
        str(ExtendedEnum.GREEN)
        # 'ExtendedEnum.GREEN'
        ExtendedEnum.GREEN.name
        # 'GREEN'
        ExtendedEnum.GREEN.value
        # 'green'
        ExtendedEnum.GREEN.description
        # 'a color of grass in the spring'
        ExtendedEnum.GREEN.describe()
        # 'a color of grass in the spring'
        ```
         In case an enum has extended set of fields it it must be guaranteed that the `__init__`
         method has the `name` argument in the last position. It's because enum instances are
         instantiated like `enumtype(*value, name=name)` where `value` is the right side of
         assignment in the code `RED = 'red', 'a color of blood'` (in case the right side is not
         a tuple-like object it is wrapped into tuple beforehand)
        - protected from modifications
        ```python
        del LightEnum.ONE
        #Traceback (most recent call last):
        #  File "<stdin>", line 1, in <module>
        #  File "fastenum.py", line 81, in __delattr__
        #    self.__restrict_modification()
        #  File "fastenum.py", line 69, in __restrict_modification
        #    raise TypeError(f'Enum-like classes strictly prohibit changing any attribute/property after they are once set')
        #TypeError: Enum-like classes strictly prohibit changing any attribute/property after they are once set
        del LightEnum.ONE.name
        #Traceback (most recent call last):
        #  File "<stdin>", line 1, in <module>
        #  File "fastenum.py", line 69, in __restrict_modification
        #    raise TypeError(f'Enum-like classes strictly prohibit changing any attribute/property after they are once set')
        #TypeError: Enum-like classes strictly prohibit changing any attribute/property after they are once set
        ExtendedEnum.GREEN.description = "I've changed my mind, it's a colour of swamps"
        #Traceback (most recent call last):
        #  File "<stdin>", line 1, in <module>
        #  File "fastenum.py", line 69, in __restrict_modification
        #    raise TypeError(f'Enum-like classes strictly prohibit changing any attribute/property after they are once set')
        #TypeError: Enum-like classes strictly prohibit changing any attribute/property after they are once set
        ```
        - protected from subclassing
        ```python
        class LightSub(LightEnum):
            FOUR: 'LightSub'
        
        #Traceback (most recent call last):
        #  File "<stdin>", line 1, in <module>
        #  File "fastenum.py", line 34, in __new__
        #    typ.__call__ = typ.__new__ = typ.get
        #  File "fastenum.py", line 76, in __setattr__
        #    self.__restrict_modification()
        #  File "fastenum.py", line 69, in __restrict_modification
        #    raise TypeError(f'Enum-like classes strictly prohibit changing any attribute/property after they are once set')
        #TypeError: Enum-like classes strictly prohibit changing any attribute/property after they are once set
        ```
        - but you could declare a class providing no new values (the result will be just an alias):
        ```python
        class LightAlias(LightEnum):
            pass
        
        LightAlias.ONE
        # <LightEnum.ONE: 1>
        ```
        - and extensible in superclasses
        ```python
        class ExtEnumBase(metaclass=FastEnum):
            description: Text
        
            __slots__ = ('description',)
        
            def __init__(self, value, description, name):
                self.value = value
                self.name = name
                self.description = description
        
        
        class ExtEnumOne(ExtEnumBase):
            ONE = 1, 'First positive integer'
            TWO = 2, 'Second positive integer'
        
        
        class ExtEnumTwo(ExtEnumBase):
            RED = 'red', 'A sunset'
            GREEN = 'green', 'Allows to cross the road'
        ```
        - as requested after publication, it's possible to subclass arbitrary classes (look at tests for more) starting from 1.3:
        ```python
        class IntEnum(int, metaclass=FastEnum):
            ONE = 1
            TWO = 2
        
        # >>> IntEnum.ONE == 1
        # True
        # >>> IntEnum.ONE * 100
        # 100
        
        import sys
        sys.exit(IntEnum.TWO)  # sets python's interpreter exit code to 2
        ```
        
        - faster than standard library's one
        ```
        In [2]: %timeit ValuesGivenEnum.FOUR
        21.4 ns ± 0.196 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
        
        In [3]: %timeit ValuesGivenEnum.FOUR.name
        30.3 ns ± 0.121 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
        
        In [4]: %timeit ValuesGivenEnum.FOUR.value
        30.4 ns ± 0.166 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
        
        In [5]: %timeit ValuesGivenEnum(4)
        111 ns ± 0.599 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
        
        In [6]: %timeit ValuesGivenEnum['FOUR']
        84.6 ns ± 0.188 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
        ```
         compare that to
        ```python
        # Classic enum
        from enum import Enum
        class StdEnum(Enum):
            ONE = 1
            TWO = 2
        ```
        ```
        In [7]: %timeit StdEnum.ONE
        69.2 ns ± 0.195 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
        
        In [8]: %timeit StdEnum.ONE.name
        247 ns ± 0.501 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
        
        In [9]: %timeit StdEnum.ONE.value
        249 ns ± 1.43 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
        
        In [10]: %timeit StdEnum(1)
        380 ns ± 3.74 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
        
        In [11]: %timeit StdEnum['ONE']
        134 ns ± 0.246 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
        ```
          That is:
           - 3 times faster on enum's member access
           - ~8.5 times faster on enum's property (`name`, `value`) access
           - 3 times faster on enum's access by val (call on enum's class `MyEnum(value)`)
           - 1.5 times faster on enum's access by name (dict-like `MyEnum[name]`)
        
Platform: any
Classifier: License :: OSI Approved :: MIT License
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Natural Language :: English
Classifier: Natural Language :: Russian
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Description-Content-Type: text/markdown
