Python独自のEnum最適化

3887 ワード

Python公式のEnum使用値のため、データベースなどを書く際に加算する必要があります.valueは、問題を起こしやすいのでenumを書き直しました.
いくつかの互換性コードがありますが、削除できます.ここでは最適化しません.
# coding: utf-8
"""
Enum  ,  python     enum34  Enum、IntEnum             .value     ,      bug,        ,Enum  
     In     ,        ,         Enum。         ,  Enum34    ,  __choices__   ,      ,      。

           enum34      ,      。
class Example(Enum):
    a = 1
    b = 2

    __choices__ = (
        (a, u'    a'),
        (b, u'    b'),
    )


>> Example['a']  #         
Out: 1
>> Example[1]  #        
Out: 1
>> c = Example[3]
Out: ValueError: 3
>> c = Example['c']
Out: KeyError: c

>> a1 = Example[1]
>> print a1
Out: 1
>> a1.label
Out: u'    a'
>> a1.name
Out: a

"""
from __future__ import unicode_literals


class NewInt(int):
    """     int      ,   label,value   """


class NewStr(str):
    """     str      ,   label,value   """


class NewUnicode(unicode):
    """     unicode      ,   label,value   """

# from enum import Enum


class EnumMeta(type):
    __choices__ = ()
    _enum_type = NewInt

    def __new__(mcs, *args, **kwargs):
        obj = type.__new__(mcs, *args, **kwargs)
        obj._value_to_label = dict(obj.__choices__)  # label
        obj._keys, obj._values = [], []
        for key, value in obj.__dict__.items():
            if not key.startswith('_'):
                if isinstance(value, (int, basestring)):
                    val = obj._enum_type(value)
                    val.name = key
                    val.value = value    # todo wangtao   .value  ,      
                    val.label = obj._value_to_label.get(val) or key
                    setattr(obj, key, val)
                    obj._keys.append(key)
                    obj._values.append(val)

        if len(set(obj._keys)) != len(set(obj._values)):  #        
            raise ValueError('duplicate value')
        return obj

    def __getitem__(self, key):
        """    name value  """
        for val in self._values:
            if val == key:
                return val

        if key in self._keys:
            return getattr(self, key, None)
        raise KeyError(key)

    def __setattr__(cls, key, value):
        if not key.startswith('_') and key in cls._keys:
            raise AttributeError('Cannot reassign members')
        return super(EnumMeta, cls).__setattr__(key, value)


class EnumMixin(object):
    __choices__ = ()

    @classmethod
    def to_dict(cls):
        if not getattr(cls, '_to_dict', None):
            cls._to_dict = dict((x.name, x.value) for x in cls._values)
        return cls._to_dict

    @classmethod
    def label_items(cls):
        return cls.__choices__

    @classmethod
    def get_choices(cls):
        return cls.__choices__

    @classmethod
    def get_display_name(cls, value):
        return cls[value].label

    @classmethod
    def all_elements(cls):
        return cls._values

    #    Enum.name('name') / Enum.value(1)        Enum['name'] , Enum[1]
    @classmethod
    def name(cls, name):
        res = getattr(cls, name)
        if res is None:
            raise KeyError(name)
        return res

    @classmethod
    def value(cls, value):
        for val in cls._values:
            if val == value:
                return val
        else:
            raise ValueError(value)

    def __new__(cls, enum_value):
        return cls[enum_value]


class Enum(EnumMixin):
    __metaclass__ = EnumMeta
    _enum_type = NewStr


class IntEnum(EnumMixin):
    __metaclass__ = EnumMeta
    _enum_type = NewInt


class UnicodeEnum(EnumMixin):
    __metaclass__ = EnumMeta
    _enum_type = NewUnicode