import functools
class EnumValue(object):def __init__(self,name,value,type):self.__value=valueself.__name=nameself.Type=typedef __str__(self):return self.__namedef __repr__(self):#2.6 only... so change to what ever you need...return '{cls}({0!r},{1!r},{2})'.format(self.__name,self.__value,self.Type.__name__,cls=type(self).__name__)
def __hash__(self):return hash(self.__value)def __nonzero__(self):return bool(self.__value)def __cmp__(self,other):if isinstance(other,EnumValue):return cmp(self.__value,other.__value)else:return cmp(self.__value,other)#hopefully their the same type... but who cares?def __or__(self,other):if other is None:return selfelif type(self) is not type(other):raise TypeError()return EnumValue('{0.Name} | {1.Name}'.format(self,other),self.Value|other.Value,self.Type)def __and__(self,other):if other is None:return selfelif type(self) is not type(other):raise TypeError()return EnumValue('{0.Name} & {1.Name}'.format(self,other),self.Value&other.Value,self.Type)def __contains__(self,other):if self.Value==other.Value:return Truereturn bool(self&other)def __invert__(self):enumerables=self.Type.__enumerables__return functools.reduce(EnumValue.__or__,(enum for enum in enumerables.itervalues() if enum not in self))
@propertydef Name(self):return self.__name
@propertydef Value(self):return self.__value
class EnumMeta(type):@staticmethoddef __addToReverseLookup(rev,value,newKeys,nextIter,force=True):if value in rev:forced,items=rev.get(value,(force,()) )if forced and force: #value was forced, so just appendrev[value]=(True,items+newKeys)elif not forced:#move it to a new spotnext=nextIter.next()EnumMeta.__addToReverseLookup(rev,next,items,nextIter,False)rev[value]=(force,newKeys)else: #not forcing this valuenext = nextIter.next()EnumMeta.__addToReverseLookup(rev,next,newKeys,nextIter,False)rev[value]=(force,newKeys)else:#set it and forget itrev[value]=(force,newKeys)return value
def __init__(cls,name,bases,atts):classVars=vars(cls)enums = classVars.get('__enumerables__',None)nextIter = getattr(cls,'__nextitr__',itertools.count)()reverseLookup={}values={}
if enums is not None:#build reverse lookupfor item in enums:if isinstance(item,(tuple,list)):items=list(item)value=items.pop()EnumMeta.__addToReverseLookup(reverseLookup,value,tuple(map(str,items)),nextIter)else:value=nextIter.next()value=EnumMeta.__addToReverseLookup(reverseLookup,value,(str(item),),nextIter,False)#add it to the reverse lookup, but don't force it to that value
#build values and clean up reverse lookupfor value,fkeys in reverseLookup.iteritems():f,keys=fkeysfor key in keys:enum=EnumValue(key,value,cls)setattr(cls,key,enum)values[key]=enumreverseLookup[value]=tuple(val for val in values.itervalues() if val.Value == value)setattr(cls,'__reverseLookup__',reverseLookup)setattr(cls,'__enumerables__',values)setattr(cls,'_Max',max([key for key in reverseLookup] or [0]))return super(EnumMeta,cls).__init__(name,bases,atts)
def __iter__(cls):for enum in cls.__enumerables__.itervalues():yield enumdef GetEnumByName(cls,name):return cls.__enumerables__.get(name,None)def GetEnumByValue(cls,value):return cls.__reverseLookup__.get(value,(None,))[0]
class Enum(object):__metaclass__=EnumMeta__enumerables__=None
class FlagEnum(Enum):@staticmethoddef __nextitr__():yield 0for val in itertools.count():yield 2**val
def enum(name,*args):return EnumMeta(name,(Enum,),dict(__enumerables__=args))
接受或离开它,它做了我需要它做的事情:)
像这样使用它:
class Air(FlagEnum):__enumerables__=('None','Oxygen','Nitrogen','Hydrogen')
class Mammals(Enum):__enumerables__=('Bat','Whale',('Dog','Puppy',1),'Cat')Bool = enum('Bool','Yes',('No',0))
class Enum(object):def __init__(self, names, separator=None):self.names = names.split(separator)for value, name in enumerate(self.names):setattr(self, name.upper(), value)def tuples(self):return tuple(enumerate(self.names))
如何使用:
>>> state = Enum('draft published retracted')>>> state.DRAFT0>>> state.RETRACTED2>>> state.FOOTraceback (most recent call last):File "<stdin>", line 1, in <module>AttributeError: 'Enum' object has no attribute 'FOO'>>> state.tuples()((0, 'draft'), (1, 'published'), (2, 'retracted'))
# an internal class, not intended to be seen by client codeclass _Constants(object):pass
# an enumeration of constants for operator associativityopAssoc = _Constants()opAssoc.LEFT = object()opAssoc.RIGHT = object()
class Enumerator(object):def __init__(self, name):self.name = name
def __eq__(self, other):if self.name == other:return Truereturn self is other
def __ne__(self, other):if self.name != other:return Falsereturn self is other
def __repr__(self):return 'Enumerator({0})'.format(self.name)
def __str__(self):return self.name
class Enum(object):def __init__(self, *enumerators):for e in enumerators:setattr(self, e, Enumerator(e))def __getitem__(self, key):return getattr(self, key)
再一次,为了配置文件或其他远程输入,我们可以自然地对字符串进行测试,这可能会更好。
示例:
class Cow(object):State = Enum('standing','walking','eating','mooing','sleeping','dead','dying')state = State.standing
In [1]: from enum import Enum
In [2]: c = Cow()
In [3]: c2 = Cow()
In [4]: c.state, c2.stateOut[4]: (Enumerator(standing), Enumerator(standing))
In [5]: c.state == c2.stateOut[5]: True
In [6]: c.State.mooingOut[6]: Enumerator(mooing)
In [7]: c.State['mooing']Out[7]: Enumerator(mooing)
In [8]: c.state = Cow.State.dead
In [9]: c.state == c2.stateOut[9]: False
In [10]: c.state == Cow.State.deadOut[10]: True
In [11]: c.state == 'dead'Out[11]: True
In [12]: c.state == Cow.State['dead']Out[11]: True
def enum(clsdef):class Enum(object):__slots__=tuple([var for var in clsdef.__dict__ if isinstance((getattr(clsdef, var)), tuple) and not var.startswith('__')])
def __new__(cls, *args, **kwargs):if not '_the_instance' in cls.__dict__:cls._the_instance = object.__new__(cls, *args, **kwargs)return cls._the_instance
def __init__(self):clsdef.values=lambda cls, e=Enum: e.values()clsdef.valueOf=lambda cls, n, e=self: e.valueOf(n)for ordinal, key in enumerate(self.__class__.__slots__):args=getattr(clsdef, key)instance=clsdef(*args)instance._name=keyinstance._ordinal=ordinalsetattr(self, key, instance)
@classmethoddef values(cls):if not hasattr(cls, '_values'):cls._values=[getattr(cls, name) for name in cls.__slots__]return cls._values
def valueOf(self, name):return getattr(self, name)
def __repr__(self):return ''.join(['<class Enum (', clsdef.__name__, ') at ', str(hex(id(self))), '>'])
return Enum()
样品使用:
i=2@enumclass Test(object):A=("a",1)B=("b",)C=("c",2)D=tuple()E=("e",3)
while True:try:F, G, H, I, J, K, L, M, N, O=[tuple() for _ in range(i)]break;except ValueError:i+=1
def __init__(self, name="default", aparam=0):self.name=nameself.avalue=aparam
def enum_repr(t, **enums):'''enums with a base class and repr() output'''class Enum(t):def __repr__(self):return '<enum {0} of type Enum({1})>'.format(self._name, t.__name__)
for key,val in enums.items():i = Enum(val)i._name = keysetattr(Enum, key, i)
return Enum
>>> Numbers = enum_repr(int, ONE=1, TWO=2, THREE=3)>>> repr(Numbers.ONE)'<enum ONE of type Enum(int)>'>>> str(Numbers.ONE)'1'
>>> State = Enum(['Unclaimed', 'Claimed'])>>> State.Claimed1>>> State[1]'Claimed'>>> State('Unclaimed', 'Claimed')>>> range(len(State))[0, 1]>>> [(k, State[k]) for k in range(len(State))][(0, 'Unclaimed'), (1, 'Claimed')]>>> [(k, getattr(State, k)) for k in State][('Unclaimed', 0), ('Claimed', 1)]
class ConstMeta(type):'''Metaclass for some class that store constants'''def __init__(cls, name, bases, dct):'''init class instance'''def static_attrs():'''@rtype: (static_attrs, static_val_set)@return: Static attributes in dict format and static value set'''import typesattrs = {}val_set = set()#Maybe morefilter_names = set(['__doc__', '__init__', '__metaclass__', '__module__', '__main__'])for key, value in dct.iteritems():if type(value) != types.FunctionType and key not in filter_names:if len(value) != 2:raise NotImplementedError('not support for values that is not 2 elements!')#Check value[0] duplication.if value[0] not in val_set:val_set.add(value[0])else:raise KeyError("%s 's key: %s is duplicated!" % (dict([(key, value)]), value[0]))attrs[key] = valuereturn attrs, val_set
attrs, val_set = static_attrs()#Set STATIC_ATTRS to class instance so that can reusesetattr(cls, 'STATIC_ATTRS', attrs)setattr(cls, 'static_val_set', val_set)super(ConstMeta, cls).__init__(name, bases, dct)
def __getattribute__(cls, name):'''Rewrite the special function so as to get correct attribute value'''static_attrs = object.__getattribute__(cls, 'STATIC_ATTRS')if name in static_attrs:return static_attrs[name][0]return object.__getattribute__(cls, name)
def static_values(cls):'''Put values in static attribute into a list, use the function to validate value.@return: Set of values'''return cls.static_val_set
def __getitem__(cls, key):'''Rewrite to make syntax SomeConstClass[key] works, and return desc string of related static value.@return: Desc string of related static value'''for k, v in cls.STATIC_ATTRS.iteritems():if v[0] == key:return v[1]raise KeyError('Key: %s does not exists in %s !' % (str(key), repr(cls)))
class Const(object):'''Base class for constant class.
@usage:
Definition: (must inherit from Const class!>>> class SomeConst(Const):>>> STATUS_NAME_1 = (1, 'desc for the status1')>>> STATUS_NAME_2 = (2, 'desc for the status2')
Invoke(base upper SomeConst class):1) SomeConst.STATUS_NAME_1 returns 12) SomeConst[1] returns 'desc for the status1'3) SomeConst.STATIC_ATTRS returns {'STATUS_NAME_1': (1, 'desc for the status1'), 'STATUS_NAME_2': (2, 'desc for the status2')}4) SomeConst.static_values() returns set([1, 2])
Attention:SomeCosnt's value 1, 2 can not be duplicated!If WrongConst is like this, it will raise KeyError:class WrongConst(Const):STATUS_NAME_1 = (1, 'desc for the status1')STATUS_NAME_2 = (1, 'desc for the status2')'''__metaclass__ = ConstMeta###################################################################Const Base Class ends##################################################################
def main():class STATUS(Const):ERROR = (-3, '??')OK = (0, '??')
print STATUS.ERRORprint STATUS.static_values()print STATUS.STATIC_ATTRS
#Usage sample:user_input = 1#Validate input:print user_input in STATUS.static_values()#Template render like:print '<select>'for key, value in STATUS.STATIC_ATTRS.items():print '<option value="%s">%s</option>' % (value[0], value[1])print '</select>'
if __name__ == '__main__':main()
def Enum(*sequential, **named):"""Generate a new enum type. Usage example:
ErrorClass = Enum('STOP','GO')print ErrorClass.find_name(ErrorClass.STOP)= "STOP"print ErrorClass.find_val("STOP")= 0ErrorClass.FOO # Raises AttributeError"""enums = { v:k for k,v in enumerate(sequential) } if not named else named
@classmethoddef find_name(cls, val):result = [ k for k,v in cls.__dict__.iteritems() if v == val ]if not len(result):raise ValueError("Value %s not found in Enum" % val)return result[0]
@classmethoddef find_val(cls, n):return getattr(cls, n)
enums['find_val'] = find_valenums['find_name'] = find_namereturn type('Enum', (), enums)
>>> from enum import Enum>>> class Colors(Enum):... red = 1... green = 2... blue = 3>>> for color in Colors: print colorColors.redColors.greenColors.blue
def enum(*names):"""SYNOPSISWell-behaved enumerated type, easier than creating custom classes
DESCRIPTIONCreate a custom type that implements an enumeration. Similar in conceptto a C enum but with some additional capabilities and protections. Seehttp://code.activestate.com/recipes/413486-first-class-enums-in-python/.
PARAMETERSnames Ordered list of names. The order in which names are givenwill be the sort order in the enum type. Duplicate namesare not allowed. Unicode names are mapped to ASCII.
RETURNSObject of type enum, with the input names and the enumerated values.
EXAMPLES>>> letters = enum('a','e','i','o','u','b','c','y','z')>>> letters.a < letters.eTrue
## index by property>>> letters.aa
## index by position>>> letters[0]a
## index by name, helpful for bridging string inputs to enum>>> letters['a']a
## sorting by order in the enum() create, not character value>>> letters.u < letters.bTrue
## normal slicing operations available>>> letters[-1]z
## error since there are not 100 items in enum>>> letters[99]Traceback (most recent call last):...IndexError: tuple index out of range
## error since name does not exist in enum>>> letters['ggg']Traceback (most recent call last):...ValueError: tuple.index(x): x not in tuple
## enums must be named using valid Python identifiers>>> numbers = enum(1,2,3,4)Traceback (most recent call last):...AssertionError: Enum values must be string or unicode
>>> a = enum('-a','-b')Traceback (most recent call last):...TypeError: Error when calling the metaclass bases__slots__ must be identifiers
## create another enum>>> tags = enum('a','b','c')>>> tags.aa>>> letters.aa
## can't compare values from different enums>>> letters.a == tags.aTraceback (most recent call last):...AssertionError: Only values from the same enum are comparable
>>> letters.a < tags.aTraceback (most recent call last):...AssertionError: Only values from the same enum are comparable
## can't update enum after create>>> letters.a = 'x'Traceback (most recent call last):...AttributeError: 'EnumClass' object attribute 'a' is read-only
## can't update enum after create>>> del letters.uTraceback (most recent call last):...AttributeError: 'EnumClass' object attribute 'u' is read-only
## can't have non-unique enum values>>> x = enum('a','b','c','a')Traceback (most recent call last):...AssertionError: Enums must not repeat values
## can't have zero enum values>>> x = enum()Traceback (most recent call last):...AssertionError: Empty enums are not supported
## can't have enum values that look like special function names## since these could collide and lead to non-obvious errors>>> x = enum('a','b','c','__cmp__')Traceback (most recent call last):...AssertionError: Enum values beginning with __ are not supported
LIMITATIONSEnum values of unicode type are not preserved, mapped to ASCII instead.
"""## must have at least one enum valueassert names, 'Empty enums are not supported'## enum values must be stringsassert len([i for i in names if not isinstance(i, types.StringTypes) and not \isinstance(i, unicode)]) == 0, 'Enum values must be string or unicode'## enum values must not collide with special function namesassert len([i for i in names if i.startswith("__")]) == 0,\'Enum values beginning with __ are not supported'## each enum value must be unique from all othersassert names == uniquify(names), 'Enums must not repeat values'
class EnumClass(object):""" See parent function for explanation """
__slots__ = names
def __iter__(self):return iter(constants)
def __len__(self):return len(constants)
def __getitem__(self, i):## this makes xx['name'] possibleif isinstance(i, types.StringTypes):i = names.index(i)## handles the more normal xx[0]return constants[i]
def __repr__(self):return 'enum' + str(names)
def __str__(self):return 'enum ' + str(constants)
def index(self, i):return names.index(i)
class EnumValue(object):""" See parent function for explanation """
__slots__ = ('__value')
def __init__(self, value):self.__value = value
value = property(lambda self: self.__value)
enumtype = property(lambda self: enumtype)
def __hash__(self):return hash(self.__value)
def __cmp__(self, other):assert self.enumtype is other.enumtype, 'Only values from the same enum are comparable'return cmp(self.value, other.value)
def __invert__(self):return constants[maximum - self.value]
def __nonzero__(self):## return bool(self.value)## Original code led to bool(x[0])==False, not correctreturn True
def __repr__(self):return str(names[self.value])
maximum = len(names) - 1constants = [None] * len(names)for i, each in enumerate(names):val = EnumValue(i)setattr(EnumClass, each, val)constants[i] = valconstants = tuple(constants)enumtype = EnumClass()return enumtype
class EnumTypeError(TypeError):pass
class Enum(object):"""Minics enum type from different languagesUsage:Letters = Enum(list('abc'))a = Letters.aprint(a in Letters) # Trueprint(54 in Letters) # False"""def __init__(self, enums):if isinstance(enums, dict):self.__dict__.update(enums)elif isinstance(enums, list) or isinstance(enums, tuple):self.__dict__.update(**dict((v,k) for k,v in enumerate(enums)))else:raise EnumTypeError
def __contains__(self, key):return key in self.__dict__.values()
def __len__(self):return len(self.__dict__.values())
if __name__ == '__main__':print('Using a dictionary to create Enum:')Letters = Enum(dict((v,k) for k,v in enumerate(list('abcde'))))a = Letters.aprint('\tIs a in e?', a in Letters)print('\tIs 54 in e?', 54 in Letters)print('\tLength of Letters enum:', len(Letters))
print('\nUsing a list to create Enum:')Letters = Enum(list('abcde'))a = Letters.aprint('\tIs a in e?', a in Letters)print('\tIs 54 in e?', 54 in Letters)print('\tLength of Letters enum:', len(Letters))
try:# make sure we raise an exception if we pass an invalid argFailure = Enum('This is a Failure')print('Failure')except EnumTypeError:print('Success!')
输出:
Using a dictionary to create Enum:Is a in e? TrueIs 54 in e? FalseLength of Letters enum: 5
Using a list to create Enum:Is a in e? TrueIs 54 in e? FalseLength of Letters enum: 5Success!