Source code for __init__

# -*- coding: utf-8 -*-
"""
Module for the classes extending the default `Enum` class. It contains 5 classes:
`ExtendedEnumMeta`, `ExtendedEnum`, `ExtendedEnum`, `LabeledEnum`, `PairEnum`
and one method `namedenum`.
"""
import sys as _sys
from collections import namedtuple
from collections.abc import Sequence
from enum import Enum, EnumMeta, _EnumDict
from functools import partial
from collections import OrderedDict

__all__ = [
    'NamedEnumMeta', 'NamedEnum', 'ExtendedEnum', 'LabeledEnum', 'PairEnum',
    'namedenum'
]


class _NamedEnumDict(_EnumDict):
    """Customizes _EnumDict, such that it allows setting the value for the keyword
    '_field_names_' and provides the functions for cleaning itself and
    converting the collection type value (except str) to NamedTuple type.
    """

    def __setitem__(self, key, value):
        """
        Makes an exception for the single underscore name '_field_names_'.

        :param key: variable or function names defined in class
        :type key: str
        :param value: values or functions
        :type value: int, str, object, ...
        """
        if key == '_field_names_':
            dict.__setitem__(self, key, value)
        else:
            super().__setitem__(key, value)

    def _clean(self):
        """Removes all the items, and set the variables '_member_names' and
        '_last_values' to empty.
        """
        # for dictionary, that's the only way to _clean it
        for name in self._member_names:
            del self[name]
        # _clean the variables as well
        self._member_names, self._last_values = [], []

    def _convert(self, tuple_cls):
        """Uses the given tuple class to _convert the items.

        :param tuple_cls: using namedtuple generated
          tuple class
        :type tuple_cls: customized tuple class
        """
        if tuple_cls is tuple or not issubclass(tuple_cls, tuple):
            raise ValueError("'tuple_cls' must be a customized tuple class "
                             "using namedtuple generated.")
        _member_names = self._member_names
        _last_values = []
        feature_num = len(getattr(tuple_cls, '_fields'))

        if feature_num == 1:
            # converting the type of the value in customized tuple
            for value in self._last_values:
                _last_values.append(tuple_cls(value))
        else:
            # converting the type of the value in customized tuple
            for value in self._last_values:
                if not isinstance(value, Sequence):
                    raise ValueError("unable to unpack the value for the fields.")
                elif isinstance(value, str):
                    _last_values.append(tuple_cls(value))
                else:
                    _last_values.append(tuple_cls(*value))
        self._clean()

        # put the converted items back, the __setitem__ function in _EnumDict
        # will fill the variables, like '_member_names' and '_last_values'
        for i, name in enumerate(_member_names):
            self[name] = _last_values[i]


[docs]class NamedEnumMeta(EnumMeta): """Extends the `EnumMeta` class for three purposes: 1. uses the `_NamedEnumDict` as the data type of the `namespace` parameter for `__new__` function, such that we can use the `namedtuple` as the data type of the value of each enumeration item. 2. provides extra functions, which is independent of the variable `_field_names_`, such as `names`, `values`, `as_dict`, `as_list`, `as_set`, `as_tuple`, `as_ordereddict`, `describe`, `gen`. The aim is extending the Enum class for complicated use cases in software development. 3. provides functions for each field name defined in class variable `_field_names_` in the NamedEnum class and its subclasses, for example: assuming `'key'` is included in `_field_names_`, then the functions for this field name are: `keys`, `from_key`, `has_key`. """ @classmethod def __prepare__(mcs, cls, bases): """Namespace hook, uses _NamedEnumDict as the type of namespace instead of _EnumDict. :param cls: name of the class to create :type cls: str :param bases: parent classes of the class to create :type bases: tuple :return: namespace dictionary :rtype: _NamedEnumDict """ # create the namespace dict enum_dict = _NamedEnumDict() # inherit previous flags and _generate_next_value_ function member_type, first_enum = mcs._get_mixins_(bases) if first_enum is not None: enum_dict['_generate_next_value_'] = getattr( first_enum, '_generate_next_value_', None) return enum_dict def __new__(mcs, name, bases, namespace): """Besides the class creation, this function also intends to create a named tuple data type depending on the given value of '_field_names_' variable to be the data type of the value of enumeration iem and add those extra functions to the class. :param name: name of the instance class :type name: str :param bases: base classes, its instance class inherits from :type bases: tuple :param namespace: contains the attributes and functions of the instance class :type namespace: dict :return: class object :rtype: object """ # if the _field_names_ not defined in the class, get it from its parent # class; otherwise uses the defined one. if '_field_names_' not in namespace: _field_names_ = bases[0].__dict__.get('_field_names_', None) else: _field_names_ = namespace['_field_names_'] # if the _field_names_ is not defined, then switch back to the normal # enum but with extended functions if _field_names_: # created the customized tuple class with the defined _field_names_ _tuple_cls = namedtuple("NamedTuple", _field_names_) if {"name", "value"}.issubset(_tuple_cls._fields): raise AttributeError("'name' or 'value' cannot be attributes") # _convert the type of the item in namespace dictionary to the named # tuple type namespace._convert(_tuple_cls) # create the class and define a class variable to hold the # customized tuple class. It's needed to return the field names. cls = super().__new__(mcs, name, bases, namespace) cls._tuple_cls = _tuple_cls # the function name formats, docstring formats and bases functions func_factory_mapping = [ ("%ss", "Collective method to return the values of the attribute `%s` " "from all the enumeration items.", mcs._field_values), ("from_%s", "Returns a tuple of the defined enumeration items regarding to " "the given `field_value` of field `%s`, if `as_tuple` is True; " "otherwise returns a generator.", mcs._from_field), ("has_%s", "Returns a boolean value which indicates if there is at least " "one enumeration item in which the value of the field `%s` " "matches the given field_value.", mcs._has_field) ] # function creation factory: create functions for each field_name for field_name in cls._fields(): for name, docstring, mcs_func in func_factory_mapping: func_name = name % field_name func_docstring = docstring % field_name setattr(cls, func_name, partial(mcs_func, cls, field_name)) # override the docstring of the partial function par_func = getattr(cls, func_name) par_func.__doc__ = func_docstring par_func.__name__ = func_name cls._tuple_cls = _tuple_cls else: cls = super().__new__(mcs, name, bases, namespace) return cls def __contains__(cls, member): """Overrides the magic method in Enum class, which doesn't support member name search from python 3.8. :param member: the lookup value :type member: str | Enum class :return: if the member is contained in the enumeration. :rtype: bool """ if isinstance(member, str): return member in cls._member_map_ return isinstance(member, cls) and member._name_ in cls._member_map_
[docs] def _fields(cls): """Returns the defined field names as a `tuple` for the enumeration class. If the variable `_field_names_` is `None` or empty value, then returns an empty `tuple`. :return: tuple of field names :rtype: tuple >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> TripleEnum._fields() ('first', 'second', 'third') >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> Triangle._fields() ('first', 'second', 'third') """ if cls._field_names_: return cls._tuple_cls._fields return tuple()
@classmethod def _field_values(mcs, cls, field_name, as_tuple=True): """Base function returns a `tuple`/`generator` containing just the value of the given field_name of all the elements from the cls. It's used to generate the particular function with name format `<field_name>s` for each `field_name`. :param cls: subclass of NamedEnum class :type cls: Enum class :param field_name: attribute's name :type field_name: str :param as_tuple: returns a tuple of the values if True; otherwise returns a generator :type as_tuple: bool :return: corresponding values of the field name in all enumeration items :rtype: tuple_, Generator_ """ g = (getattr(item.value, field_name) for item in cls.gen(name_value_pair=False)) return tuple(g) if as_tuple else g @classmethod def _from_field(mcs, cls, field_name, field_value, as_tuple=True): """Base function returns a `tuple` of the defined enumeration items regarding to the given `field_value` of field with `field_name`, if `as_tuple` is True; otherwise returns a generator. It's used to generate the particular function with name format `from_<field_name>` for each `field_name`. :param cls: subclass of NamedEnum class :type cls: Enum class :param field_name: attribute's name :type field_name: str :param field_value: key to search for :type field_value: int, str, object, ... :param as_tuple: returns a tuple of the values if True; otherwise returns a generator :type as_tuple: bool :return: collection of enumeration items matching the condition :rtype: tuple_, Generator_ """ g = (item for item in cls.gen(name_value_pair=False) if getattr(item.value, field_name) == field_value) return tuple(g) if as_tuple else g @classmethod def _has_field(mcs, cls, field_name, field_value): """Base function returns a boolean value which indicates if there is at least one enumeration item in which the value of the field `field_name` corresponding value matches the given `field_value`. It's used to generate the particular function with name format `has_<field_name>` for each `field_name`. :param cls: subclass of NamedEnum class :type cls: Enum class :param field_name: attribute's name :type field_name: str :param field_value: key to search for :type field_value: int, str, object, ... :return: True, if has at least one matching; otherwise False. :rtype: bool """ gen_field_values = mcs._field_values(cls, field_name, as_tuple=False) return field_value in gen_field_values
[docs] def gen(cls, name_value_pair=True): """Returns a generator of pairs consisting of each enumeration item's name and value, if name_value_pair is True; otherwise a generator of the enumeration items. :param name_value_pair: controls the return result. If true, returns the generator of name-value pair; if False, returns the generator of the enumeration items. :type name_value_pair: bool :return: a generator which iterates all the enumeration items :rtype: Generator_ >>> from types import GeneratorType >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> isinstance(TripleEnum.gen(), GeneratorType) True >>> list(TripleEnum.gen()) [] >>> isinstance(TripleEnum.gen(name_value_pair=False), GeneratorType) True >>> list(TripleEnum.gen(name_value_pair=False)) [] >>> isinstance(Triangle.gen(), GeneratorType) True >>> list(Triangle.gen()) [('EQUILATERAL', NamedTuple(first=6, second=6, third=6)), ('RIGHT', NamedTuple(first=3, second=4, third=5))] >>> isinstance(Triangle.gen(name_value_pair=False), GeneratorType) True >>> list(Triangle.gen(name_value_pair=False)) [<Triangle.EQUILATERAL: NamedTuple(first=6, second=6, third=6)>, <Triangle.RIGHT: NamedTuple(first=3, second=4, third=5)>] """ if name_value_pair: return ((name, item.value) for name, item in cls._member_map_.items()) return (item for name, item in cls._member_map_.items())
def _as_data_type(cls, data_type): """Base function converts the enumeration class to the given data type value. It's used for generating the functions like `as_dict`, `as_tuple`, `as_set`, `as_list`, `as_ordereddict`. :param cls: subclass of NamedEnum class :type cls: Enum class :param data_type: desired data type for the output :type data_type: dict, list, set, tuple, OrderedDict_ :return: converted value depending on the given data type :rtype: dict, list, set, tuple, OrderedDict_ """ return data_type(cls.gen(name_value_pair=True))
[docs] def as_dict(cls): """Converts the enumeration to a `dict`, in which the key is the name of the enumeration item and value is its value. :return: a dictionary containing name-value-pairs of the enumeration :rtype: dict >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.as_dict() {} >>> Triangle.as_dict() {'EQUILATERAL': NamedTuple(first=6, second=6, third=6), 'RIGHT': NamedTuple(first=3, second=4, third=5)} """ return cls._as_data_type(dict)
[docs] def as_tuple(cls): """ Converts the enumerations to a `tuple`, in which each item is a tuple of the enumeration item's name and value. :return: a tuple containing name-value-pairs of the enumeration :rtype: tuple >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.as_tuple() () >>> Triangle.as_tuple() (('EQUILATERAL', NamedTuple(first=6, second=6, third=6)), ('RIGHT', NamedTuple(first=3, second=4, third=5))) """ return cls._as_data_type(tuple)
[docs] def as_set(cls): """ Converts the enumerations to a `set`, in which each item is a tuple of the enumeration item's name and value. :return: a set containing name-value-pairs of the enumeration :rtype: set >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.as_set() == set() True >>> isinstance(Triangle.as_set(), set) True >>> dict(Triangle.as_set()) == Triangle.as_dict() True """ return cls._as_data_type(set)
[docs] def as_list(cls): """ Converts the enumerations to a `list`, in which each item is a tuple of the enumeration item's name and value. :return: a list containing name-value-pairs of the enumeration :rtype: list >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.as_list() [] >>> Triangle.as_list() [('EQUILATERAL', NamedTuple(first=6, second=6, third=6)), ('RIGHT', NamedTuple(first=3, second=4, third=5))] """ return cls._as_data_type(list)
[docs] def as_ordereddict(cls): """ Converts the enumerations to an `OrderedDict`, in which each item is a tuple of the enumeration item's name and value. :return: an OrderedDict containing name-value-pairs of the enumeration :rtype: OrderedDict_ >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.as_ordereddict() OrderedDict() >>> Triangle.as_ordereddict() OrderedDict([('EQUILATERAL', NamedTuple(first=6, second=6, third=6)), ('RIGHT', NamedTuple(first=3, second=4, third=5))]) """ return cls._as_data_type(OrderedDict)
def __repr__(cls): """ Overrides the __repr__ function from EnumMeta class. :return: string represents the class :rtype: str """ return "<named enum %r>" % cls.__name__
[docs] def describe(cls): """ Prints in the console a table showing the content of the enumeration. >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.describe() Class: TripleEnum Name | First | Second | Third ----------------------------- <BLANKLINE> >>> Triangle.describe() Class: Triangle Name | First | Second | Third ------------------------------------ EQUILATERAL | 6 | 6 | 6 RIGHT | 3 | 4 | 5 <BLANKLINE> """ name = "name" max_lengths, headers = [], [] fields = cls._fields() if cls._fields() else ("value",) row_format = ["{:>%d}"] * (len(fields) + 1) names = [name] + list(cls.names()) max_lengths.append(max(list(map(len, names)))) headers.append(name.capitalize()) for attr_name in fields: attr_func = "%ss" % attr_name attr_list = list(map(str, getattr(cls, attr_func)())) max_lengths.append(max(list(map(len, [attr_name] + attr_list)))) headers.append(attr_name.capitalize()) row_format = " | ".join(row_format) % tuple(max_lengths) header_line = row_format.format(*headers) output = "Class: %s\n" % cls.__name__ output += header_line + "\n" output += "-" * (len(header_line)) + "\n" if cls._fields(): for name, value in cls.gen(): output += row_format.format(name, *value) + "\n" else: for name, value in cls.gen(): output += row_format.format(name, str(value)) + "\n" print(output)
[docs] def names(cls, as_tuple=True): """ Returns the names of all the enumeration items as a `tuple`, if parameter `as_tuple` is `True`; otherwise returns a generator. :param as_tuple: returns a tuple if True; otherwise returns a generator. :type as_tuple: bool :return: names of all the enumeration items inside the class in a specific form :rtype: tuple_, Generator_ >>> from types import GeneratorType >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.names() () >>> isinstance(TripleEnum.names(as_tuple=False), GeneratorType) True >>> list(TripleEnum.names(as_tuple=False)) [] >>> Triangle.names() ('EQUILATERAL', 'RIGHT') >>> isinstance(Triangle.names(as_tuple=False), GeneratorType) True >>> list(Triangle.names(as_tuple=False)) ['EQUILATERAL', 'RIGHT'] """ g = (name for name in cls._member_map_.keys()) return tuple(g) if as_tuple else g
[docs] def values(cls, as_tuple=True): """ Returns the values of all the enumeration items as a tuple, if parameter `as_tuple` is `True`, otherwise returns a generator. :param as_tuple: returns a tuple if True; otherwise returns a generator :type as_tuple: bool :return: values of all the enumeration items inside the class in a specific form :rtype: tuple_, Generator_ >>> from types import GeneratorType >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> TripleEnum.values() () >>> isinstance(TripleEnum.values(as_tuple=False), GeneratorType) True >>> list(TripleEnum.values(as_tuple=False)) [] >>> Triangle.values() (NamedTuple(first=6, second=6, third=6), NamedTuple(first=3, second=4, third=5)) >>> isinstance(Triangle.values(as_tuple=False), GeneratorType) True >>> list(Triangle.values(as_tuple=False)) [NamedTuple(first=6, second=6, third=6), NamedTuple(first=3, second=4, third=5)] """ g = (item.value for item in cls._member_map_.values()) return tuple(g) if as_tuple else g
[docs]class NamedEnum(Enum, metaclass=NamedEnumMeta): """ Through the value of variable `_field_names_` to control its subclass for different use cases: 1. value of `_field_names_` is `None` or empty. In this case, its subclass works like an extended Enum class with extra function: `names`, `values`, `as_dict`, `as_list`, `as_set`, `as_tuple`, `as_ordereddict`, `describe`. 2. value of `_field_names_` is neither `None` or empty. In this case, its subclass keeps the extra functions mentioned in **1**, and gives each element in the enumeration item's value a name and provides functions for each attribute/field, like: `<field_name>s`, `from_<field_name>`, `has_<field_name>`. Instead of the setting the attributes to the enumeration instance, it uses the function `__getattr__` to achieve it. >>> class TripleEnum(NamedEnum): ... _field_names_ = ("first", "second", "third") >>> class Triangle(TripleEnum): ... EQUILATERAL = (6, 6, 6) ... RIGHT = (3, 4, 5) >>> Triangle._fields() ('first', 'second', 'third') >>> Triangle.names() ('EQUILATERAL', 'RIGHT') >>> Triangle.values() (NamedTuple(first=6, second=6, third=6), NamedTuple(first=3, second=4, third=5)) >>> Triangle.describe() Class: Triangle Name | First | Second | Third ------------------------------------ EQUILATERAL | 6 | 6 | 6 RIGHT | 3 | 4 | 5 <BLANKLINE> >>> Triangle.as_dict() {'EQUILATERAL': NamedTuple(first=6, second=6, third=6), 'RIGHT': NamedTuple(first=3, second=4, third=5)} >>> Triangle.as_list() [('EQUILATERAL', NamedTuple(first=6, second=6, third=6)), ('RIGHT', NamedTuple(first=3, second=4, third=5))] >>> Triangle.as_set() == {('RIGHT', Triangle._tuple_cls(first=3, second=4, third=5)), ('EQUILATERAL', Triangle._tuple_cls(first=6, second=6, third=6))} True >>> Triangle.as_tuple() (('EQUILATERAL', NamedTuple(first=6, second=6, third=6)), ('RIGHT', NamedTuple(first=3, second=4, third=5))) >>> Triangle.as_ordereddict() OrderedDict([('EQUILATERAL', NamedTuple(first=6, second=6, third=6)), ('RIGHT', NamedTuple(first=3, second=4, third=5))]) >>> Triangle.firsts() (6, 3) >>> Triangle.seconds() (6, 4) >>> Triangle.thirds() (6, 5) >>> Triangle.from_first(6) (<Triangle.EQUILATERAL: NamedTuple(first=6, second=6, third=6)>,) >>> Triangle.from_first(66) () >>> Triangle.from_second(6) (<Triangle.EQUILATERAL: NamedTuple(first=6, second=6, third=6)>,) >>> Triangle.from_second(66) () >>> Triangle.from_third(6) (<Triangle.EQUILATERAL: NamedTuple(first=6, second=6, third=6)>,) >>> Triangle.from_third(66) () >>> Triangle.has_first(6) True >>> Triangle.has_first(66) False >>> Triangle.has_second(6) True >>> Triangle.has_second(66) False >>> Triangle.has_third(6) True >>> Triangle.has_third(66) False >>> Triangle.RIGHT <Triangle.RIGHT: NamedTuple(first=3, second=4, third=5)> >>> Triangle.RIGHT.first 3 >>> Triangle.RIGHT.second 4 >>> Triangle.RIGHT.third 5 >>> Triangle.RIGHT.name 'RIGHT' >>> Triangle.RIGHT.value NamedTuple(first=3, second=4, third=5) >>> print(Triangle.RIGHT) Triangle.RIGHT: NamedTuple(first=3, second=4, third=5) """ _field_names_ = None """ The place to define the field names of the enumeration class. It accepts the same format as the parameter `field_names` in function `namedtuple` from `collections` package. It's used in the NamedEnumMeta class's `__new__` function to generate the corresponding functions for each field. If it's value is `None` or empty, then the enumeration class behaves like a normal `Enum` class, but with some extended functions to simplify the usages of enumerations. **Attention**: this variable should not be used to get the field_names, to do so you can use the class method `_fields`. Because it also accept the comma separated string. """ def __getattr__(self, item): """ Hijacks the default __getattr__ function, such that every time when the user wants to get the value of a field in an enumeration item, it returns the corresponding field's value from the value of enumeration. :param item: name of the field or attribute :type item: str :return: corresponding value :rtype: int, str, object, ... """ if item in self.__class__._fields(): return getattr(self._value_, item) return super().__getattribute__(item) def __str__(self): """ Displays the value as well. :return: string represents the enumeration item :rtype: str """ return "%s.%s: %r" % ( self.__class__.__name__, self._name_, self._value_)
[docs]class ExtendedEnum(NamedEnum): """ An alias for the class `NamedEnum`. The goal is explicit directly providing the users an Enum class with extra functions. >>> from types import GeneratorType >>> class TVCouple(ExtendedEnum): ... GALLAGHERS = ("FRANK", "MONICA") ... MIKE_AND_MOLLY = ("Mike", "Molly") >>> TVCouple.names() ('GALLAGHERS', 'MIKE_AND_MOLLY') >>> isinstance(TVCouple.names(as_tuple=False), GeneratorType) True >>> list(TVCouple.names(as_tuple=False)) ['GALLAGHERS', 'MIKE_AND_MOLLY'] >>> TVCouple.values() (('FRANK', 'MONICA'), ('Mike', 'Molly')) >>> isinstance(TVCouple.values(as_tuple=False), GeneratorType) True >>> list(TVCouple.values(as_tuple=False)) [('FRANK', 'MONICA'), ('Mike', 'Molly')] >>> TVCouple.describe() Class: TVCouple Name | Value ------------------------------------ GALLAGHERS | ('FRANK', 'MONICA') MIKE_AND_MOLLY | ('Mike', 'Molly') <BLANKLINE> >>> isinstance(TVCouple.gen(), GeneratorType) True >>> tuple(TVCouple.gen()) (('GALLAGHERS', ('FRANK', 'MONICA')), ('MIKE_AND_MOLLY', ('Mike', 'Molly'))) >>> isinstance(TVCouple.gen(name_value_pair=False), GeneratorType) True >>> tuple(TVCouple.gen(name_value_pair=False)) (<TVCouple.GALLAGHERS: ('FRANK', 'MONICA')>, <TVCouple.MIKE_AND_MOLLY: ('Mike', 'Molly')>) >>> TVCouple.as_dict() {'GALLAGHERS': ('FRANK', 'MONICA'), 'MIKE_AND_MOLLY': ('Mike', 'Molly')} >>> isinstance(TVCouple.as_set(), set) True >>> sorted(list(TVCouple.as_set())) [('GALLAGHERS', ('FRANK', 'MONICA')), ('MIKE_AND_MOLLY', ('Mike', 'Molly'))] >>> TVCouple.as_tuple() (('GALLAGHERS', ('FRANK', 'MONICA')), ('MIKE_AND_MOLLY', ('Mike', 'Molly'))) >>> TVCouple.as_list() [('GALLAGHERS', ('FRANK', 'MONICA')), ('MIKE_AND_MOLLY', ('Mike', 'Molly'))] >>> TVCouple.as_ordereddict() OrderedDict([('GALLAGHERS', ('FRANK', 'MONICA')), ('MIKE_AND_MOLLY', ('Mike', 'Molly'))]) """ pass
[docs]class LabeledEnum(NamedEnum): """ An enumeration class with two attributes `key` and `label`. It can be used in the Django project as the choices of a field in model or form. >>> from types import GeneratorType >>> class NBALegendary(LabeledEnum): ... JOHNSON = ("Johnson", "Magic Johnson") ... Jordan = ("Jordan", "Air Jordan") >>> NBALegendary.names() ('JOHNSON', 'Jordan') >>> isinstance(NBALegendary.names(as_tuple=False), GeneratorType) True >>> list(NBALegendary.names(as_tuple=False)) ['JOHNSON', 'Jordan'] >>> NBALegendary.values() (NamedTuple(key='Johnson', label='Magic Johnson'), NamedTuple(key='Jordan', label='Air Jordan')) >>> isinstance(NBALegendary.values(as_tuple=False), GeneratorType) True >>> list(NBALegendary.values(as_tuple=False)) [NamedTuple(key='Johnson', label='Magic Johnson'), NamedTuple(key='Jordan', label='Air Jordan')] >>> NBALegendary.describe() Class: NBALegendary Name | Key | Label --------------------------------- JOHNSON | Johnson | Magic Johnson Jordan | Jordan | Air Jordan <BLANKLINE> >>> isinstance(NBALegendary.gen(), GeneratorType) True >>> tuple(NBALegendary.gen()) (('JOHNSON', NamedTuple(key='Johnson', label='Magic Johnson')), ('Jordan', NamedTuple(key='Jordan', label='Air Jordan'))) >>> isinstance(NBALegendary.gen(name_value_pair=False), GeneratorType) True >>> tuple(NBALegendary.gen(name_value_pair=False)) (<NBALegendary.JOHNSON: NamedTuple(key='Johnson', label='Magic Johnson')>, <NBALegendary.Jordan: NamedTuple(key='Jordan', label='Air Jordan')>) >>> NBALegendary.as_dict() {'JOHNSON': NamedTuple(key='Johnson', label='Magic Johnson'), 'Jordan': NamedTuple(key='Jordan', label='Air Jordan')} >>> isinstance(NBALegendary.as_set(), set) True >>> sorted(list(NBALegendary.as_set())) [('JOHNSON', NamedTuple(key='Johnson', label='Magic Johnson')), ('Jordan', NamedTuple(key='Jordan', label='Air Jordan'))] >>> NBALegendary.as_tuple() (('JOHNSON', NamedTuple(key='Johnson', label='Magic Johnson')), ('Jordan', NamedTuple(key='Jordan', label='Air Jordan'))) >>> NBALegendary.as_list() [('JOHNSON', NamedTuple(key='Johnson', label='Magic Johnson')), ('Jordan', NamedTuple(key='Jordan', label='Air Jordan'))] >>> NBALegendary.as_ordereddict() OrderedDict([('JOHNSON', NamedTuple(key='Johnson', label='Magic Johnson')), ('Jordan', NamedTuple(key='Jordan', label='Air Jordan'))]) >>> NBALegendary.keys() ('Johnson', 'Jordan') >>> NBALegendary.labels() ('Magic Johnson', 'Air Jordan') >>> isinstance(NBALegendary.keys(as_tuple=False), GeneratorType) True >>> list(NBALegendary.keys(as_tuple=False)) ['Johnson', 'Jordan'] >>> isinstance(NBALegendary.labels(as_tuple=False), GeneratorType) True >>> list(NBALegendary.labels(as_tuple=False)) ['Magic Johnson', 'Air Jordan'] >>> NBALegendary.from_key('Johnson') (<NBALegendary.JOHNSON: NamedTuple(key='Johnson', label='Magic Johnson')>,) >>> NBALegendary.from_key('Jordan') (<NBALegendary.Jordan: NamedTuple(key='Jordan', label='Air Jordan')>,) >>> NBALegendary.from_label('Magic Johnson') (<NBALegendary.JOHNSON: NamedTuple(key='Johnson', label='Magic Johnson')>,) >>> NBALegendary.from_label('Air Jordan') (<NBALegendary.Jordan: NamedTuple(key='Jordan', label='Air Jordan')>,) >>> isinstance(NBALegendary.from_key('Johnson', as_tuple=False), GeneratorType) True >>> list(NBALegendary.from_key('Johnson', as_tuple=False)) [<NBALegendary.JOHNSON: NamedTuple(key='Johnson', label='Magic Johnson')>] >>> isinstance(NBALegendary.from_key('Jordan', as_tuple=False), GeneratorType) True >>> list(NBALegendary.from_key('Jordan', as_tuple=False)) [<NBALegendary.Jordan: NamedTuple(key='Jordan', label='Air Jordan')>] >>> isinstance(NBALegendary.from_label('Magic Johnson', as_tuple=False), GeneratorType) True >>> list(NBALegendary.from_label('Magic Johnson', as_tuple=False)) [<NBALegendary.JOHNSON: NamedTuple(key='Johnson', label='Magic Johnson')>] >>> isinstance(NBALegendary.from_label('Air Jordan', as_tuple=False), GeneratorType) True >>> list(NBALegendary.from_label('Air Jordan', as_tuple=False)) [<NBALegendary.Jordan: NamedTuple(key='Jordan', label='Air Jordan')>] >>> NBALegendary.has_key('Johnson') True >>> NBALegendary.has_key('John') False >>> NBALegendary.has_key('Jordan') True >>> NBALegendary.has_key('George') False >>> NBALegendary.has_label('Magic Johnson') True >>> NBALegendary.has_label('King James') False >>> NBALegendary.has_label('Air Jordan') True >>> NBALegendary.has_label('The Black Mamba') False """ _field_names_ = ("key", "label") """Each enumeration of LabeledEnum has two attributes: `key`, `label`"""
[docs]class PairEnum(NamedEnum): """ Enumeration with two attributes `first`, `second`, the idea comes from the C++'s pair container. >>> from types import GeneratorType >>> class Pair(PairEnum): ... TOM_AND_JERRY = ("Tom", "Jerry") ... BULLS = ("Micheal", "Pippen") >>> Pair.names() ('TOM_AND_JERRY', 'BULLS') >>> isinstance(Pair.names(as_tuple=False), GeneratorType) True >>> list(Pair.names(as_tuple=False)) ['TOM_AND_JERRY', 'BULLS'] >>> Pair.values() (NamedTuple(first='Tom', second='Jerry'), NamedTuple(first='Micheal', second='Pippen')) >>> isinstance(Pair.values(as_tuple=False), GeneratorType) True >>> list(Pair.values(as_tuple=False)) [NamedTuple(first='Tom', second='Jerry'), NamedTuple(first='Micheal', second='Pippen')] >>> Pair.describe() Class: Pair Name | First | Second -------------------------------- TOM_AND_JERRY | Tom | Jerry BULLS | Micheal | Pippen <BLANKLINE> >>> isinstance(Pair.gen(), GeneratorType) True >>> tuple(Pair.gen()) (('TOM_AND_JERRY', NamedTuple(first='Tom', second='Jerry')), ('BULLS', NamedTuple(first='Micheal', second='Pippen'))) >>> isinstance(Pair.gen(name_value_pair=False), GeneratorType) True >>> tuple(Pair.gen(name_value_pair=False)) (<Pair.TOM_AND_JERRY: NamedTuple(first='Tom', second='Jerry')>, <Pair.BULLS: NamedTuple(first='Micheal', second='Pippen')>) >>> Pair.as_dict() {'TOM_AND_JERRY': NamedTuple(first='Tom', second='Jerry'), 'BULLS': NamedTuple(first='Micheal', second='Pippen')} >>> isinstance(Pair.as_set(), set) True >>> sorted(list(Pair.as_set())) [('BULLS', NamedTuple(first='Micheal', second='Pippen')), ('TOM_AND_JERRY', NamedTuple(first='Tom', second='Jerry'))] >>> Pair.as_tuple() (('TOM_AND_JERRY', NamedTuple(first='Tom', second='Jerry')), ('BULLS', NamedTuple(first='Micheal', second='Pippen'))) >>> Pair.as_list() [('TOM_AND_JERRY', NamedTuple(first='Tom', second='Jerry')), ('BULLS', NamedTuple(first='Micheal', second='Pippen'))] >>> Pair.as_ordereddict() OrderedDict([('TOM_AND_JERRY', NamedTuple(first='Tom', second='Jerry')), ('BULLS', NamedTuple(first='Micheal', second='Pippen'))]) >>> Pair.firsts() ('Tom', 'Micheal') >>> Pair.seconds() ('Jerry', 'Pippen') >>> isinstance(Pair.firsts(as_tuple=False), GeneratorType) True >>> list(Pair.firsts(as_tuple=False)) ['Tom', 'Micheal'] >>> isinstance(Pair.seconds(as_tuple=False), GeneratorType) True >>> list(Pair.seconds(as_tuple=False)) ['Jerry', 'Pippen'] >>> Pair.from_first("Tom") (<Pair.TOM_AND_JERRY: NamedTuple(first='Tom', second='Jerry')>,) >>> Pair.from_first("Micheal") (<Pair.BULLS: NamedTuple(first='Micheal', second='Pippen')>,) >>> Pair.from_second("Jerry") (<Pair.TOM_AND_JERRY: NamedTuple(first='Tom', second='Jerry')>,) >>> Pair.from_second("Pippen") (<Pair.BULLS: NamedTuple(first='Micheal', second='Pippen')>,) >>> isinstance(Pair.from_first("Tom", as_tuple=False), GeneratorType) True >>> list(Pair.from_first("Tom", as_tuple=False)) [<Pair.TOM_AND_JERRY: NamedTuple(first='Tom', second='Jerry')>] >>> isinstance(Pair.from_first("Micheal", as_tuple=False), GeneratorType) True >>> list(Pair.from_first("Micheal", as_tuple=False)) [<Pair.BULLS: NamedTuple(first='Micheal', second='Pippen')>] >>> isinstance(Pair.from_second("Jerry", as_tuple=False), GeneratorType) True >>> list(Pair.from_second("Jerry", as_tuple=False)) [<Pair.TOM_AND_JERRY: NamedTuple(first='Tom', second='Jerry')>] >>> isinstance(Pair.from_second("Pippen", as_tuple=False), GeneratorType) True >>> list(Pair.from_second("Pippen", as_tuple=False)) [<Pair.BULLS: NamedTuple(first='Micheal', second='Pippen')>] >>> Pair.has_first('Tom') True >>> Pair.has_first('Tommy') False >>> Pair.has_first('Micheal') True >>> Pair.has_first('Mike') False >>> Pair.has_second('Jerry') True >>> Pair.has_second('Jeremy') False >>> Pair.has_second('Pippen') True >>> Pair.has_second('Pepe') False """ _field_names_ = ("first", "second") """Each enumeration of PairEnum has two attributes: first, second"""
_class_template = """\ from named_enum import NamedEnum class {typename}(NamedEnum): _field_names_ = {field_names!r} """
[docs]def namedenum(typename, field_names=None, *, verbose=False, module=None): """ Creates an named enum class with the given typename as class name and field_names as the _field_names_ in named enum class. The implementation is similar to the namedtuple function. :param typename: name for the created class :type typename: str :param field_names: field names for the named enum class :type field_names: Sequence_ :param verbose: displays the code for the named enum class creation, if True :type verbose: bool :param module: which module the new created enum class belongs to :type module: None, str :return: subclass of NamedEnum :rtype: object >>> TripleEnum = namedenum("TripleEnum", ("first", "second", "third")) >>> TripleEnum <named enum 'TripleEnum'> """ # Fill-in the class template class_definition = _class_template.format( typename=typename, field_names=field_names ) # Execute the template string in a temporary namespace and support # tracing utilities by setting a value for frame.f_globals['__name__'] namespace = dict(__name__='%s' % typename) exec(class_definition, namespace) result = namespace[typename] result._source = class_definition if verbose: print(result._source) # For pickling to work, the __module__ variable needs to be set to the frame # where the named tuple is created. Bypass this step in environments where # sys._getframe is not defined (Jython for example) or sys._getframe is not # defined for arguments greater than 0 (IronPython), or where the user has # specified a particular module. if module is None: try: module = _sys._getframe(1).f_globals.get('__name__', '__main__') except (AttributeError, ValueError): pass if module is not None: result.__module__ = module return result