将枚举成员的值自动设置为其名称 [英] Automatically setting an enum member's value to its name

查看:166
本文介绍了将枚举成员的值自动设置为其名称的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在摸索python的枚举图书馆,并遇到了一个难题。在文档中,它们显示了一个自动编号枚举的示例,其中定义了一些东西:

  class Color(AutoNumber):
red =()
green = )
...

我想做一个类似的类,但是值会自动从成员的名称设置,并保持您从 str 枚举获得的功能 mixin stuff



所以类似:

  class Animal(MagicStrEnum):
horse =()
dog =()

Animal.dog =='dog'#True

我看了在枚举模块的源代码中,尝试了很多变体,它们围绕着 __ new __ EnumMeta class


决方案

更新:2017-03-01


在Python 3.6(和 Aenum 2.0 1 标记 IntFlag 类已添加;其中一部分是新的 auto( )帮助器,这使得这很简单:




 >>> class AutoName(Enum):
... def _generate_next_value_(name,start,count,last_values):
...返回名称
...
>> ; class Ordinal(AutoName):
... NORTH = auto()
... SOUTH = auto()
... EAST = auto()
... WEST = auto()
...
>>> list(Ordinal)
[< Ordinal.NORTH:'NORTH'>,< Ordinal.SOUTH:'SOUTH'>,< Ordinal.EAST:'EAST'>,< Ordinal.WEST :'WEST'>]






原始答案



使用 AutoStr 类的难点是枚举成员的名称不是传入创建它的代码,所以它不可用。另一个皱纹是 str 是不可变的,所以我们不能在创建枚举后改变这些类型的枚举(通过使用 class decorator )。



最简单的做法是使用功能API

  Animal = Enum('Animal',[(a,a)for a('horse','dog')],type = str)

这给了我们:

 >>> list(Animal)
[< Animal.horse:'horse'>,< Animal.dog:'dog'>]

>>> Animal.dog =='dog'
True






下一个最简单的事情,假设你想为你今后的枚举使用一个基类,就像我的 DocEnem

  class DocEnum(Enum):

比较相当于其名称的所有封装版本
接受

def __new __(cls,* args):
忽略参数(将在__init__中处理)
obj =对象.__新__(cls)
obj._value_ =无
返回obj

def __init __(self,doc = None):
#first,fix _value_
self._value_ = self._name_.lower()
self .__ doc__ = doc

def __eq __(self,other):
if isinstance(other,basestring):
return self._value_ == other.lower()
elif not isinstance(other,self .__ class__):
return NotImplement ed
return self is other

def __ne __(self,other):
return not self == other

并正在使用:

  class SpecKind(DocEnum):
REQUIRED =required value
OPTION =每个名称的单个值
MULTI =每个名称的多个值(列表形式)
FLAG =每个名称的布尔值
KEYWORD ='未知选项'

请注意,与第一个选项不同, DocEnum 成员不是 str s。






如果你想做的很难的方式:子类 EnumMeta 并提出新的枚举的类字典之前的成员被创建:

 从枚举导入EnumMeta, enum,_EnumDict 

class StrEnumMeta(EnumMeta):
def __new __(metacls,cls,bases,oldclassdict):

扫描oldclassdict和合作将任何一个简单元组
的值转换为名称中的str,而不是

newclassdict = _EnumDict()
for k,v in oldclassdict.items )
如果v ==():
v = k
newclassdict [k] = v
return super().__ new __(metacls,cls,bases,newclassdict)

class AutoStrEnum(str,Enum,metaclass = StrEnumMeta):
name = value str enums的基类

class Animal(AutoStrEnum):
马=()
dog =()
whale =()

print(Animal.horse)
print(Animal.horse =='horse')
print(Animal.horse.name,Animal.horse.value)

哪个给我们:

  Animal.horse 
True
马马
/ pre>




1 披露:我是 Python stdlib 枚举 enum34 backport 高级枚举( aenum 库。


I've been messing around with python's enum library and have come across a conundrum. In the docs, they show an example of an auto-numbering enum, wherein something is defined:

class Color(AutoNumber):
    red = ()
    green = ()
    ...

I want to make a similar class, but the value would automatically be set from the name of the member AND keep the functionality that you get from doing the str and enum mixin stuff

So something like:

class Animal(MagicStrEnum):
    horse = ()
    dog = ()

Animal.dog == 'dog' # True

I've looked at the source code of the enum module and tried a lot of variations messing around with __new__ and the EnumMeta class

解决方案

Update: 2017-03-01

In Python 3.6 (and Aenum 2.01) Flag and IntFlag classes have been added; part of that was a new auto() helper that makes this trivially easy:

>>> class AutoName(Enum):
...     def _generate_next_value_(name, start, count, last_values):
...         return name
...
>>> class Ordinal(AutoName):
...     NORTH = auto()
...     SOUTH = auto()
...     EAST = auto()
...     WEST = auto()
...
>>> list(Ordinal)
[<Ordinal.NORTH: 'NORTH'>, <Ordinal.SOUTH: 'SOUTH'>, <Ordinal.EAST: 'EAST'>, <Ordinal.WEST: 'WEST'>]


Original answer

The difficulty with an AutoStr class is that the name of the enum member is not passed into the code that creates it, so it is unavailable for use. Another wrinkle is that str is immutable, so we can't change those types of enums after they have been created (by using a class decorator, for example).

The easiest thing to do is use the Functional API:

Animal = Enum('Animal', [(a, a) for a in ('horse', 'dog')], type=str)

which gives us:

>>> list(Animal)
[<Animal.horse: 'horse'>, <Animal.dog: 'dog'>]

>>> Animal.dog == 'dog'
True


The next easiest thing to do, assuming you want to make a base class for your future enumeration use, would be something like my DocEnem:

class DocEnum(Enum):
    """
    compares equal to all cased versions of its name
    accepts a doctring for each member
    """
    def __new__(cls, *args):
        """Ignores arguments (will be handled in __init__)"""
        obj = object.__new__(cls)
        obj._value_ = None
        return obj

    def __init__(self, doc=None):
        # first, fix _value_
        self._value_ = self._name_.lower()
        self.__doc__ = doc

    def __eq__(self, other):
        if isinstance(other, basestring):
            return self._value_ == other.lower()
        elif not isinstance(other, self.__class__):
            return NotImplemented
        return self is other

    def __ne__(self, other):
        return not self == other

and in use:

class SpecKind(DocEnum):
    REQUIRED = "required value"
    OPTION = "single value per name"
    MULTI = "multiple values per name (list form)"
    FLAG = "boolean value per name"
    KEYWORD = 'unknown options'

Note that unlike the first option, DocEnum members are not strs.


If you want to do it the hard way: subclass EnumMeta and fiddle with the new Enum's class dictionary before the members are created:

from enum import EnumMeta, Enum, _EnumDict

class StrEnumMeta(EnumMeta):
    def __new__(metacls, cls, bases, oldclassdict):
        """
        Scan through `oldclassdict` and convert any value that is a plain tuple
        into a `str` of the name instead
        """
        newclassdict = _EnumDict()
        for k, v in oldclassdict.items():
            if v == ():
                v = k
            newclassdict[k] = v
        return super().__new__(metacls, cls, bases, newclassdict)

class AutoStrEnum(str, Enum, metaclass=StrEnumMeta):
    "base class for name=value str enums"

class Animal(AutoStrEnum):
    horse = ()
    dog = ()
    whale = ()

print(Animal.horse)
print(Animal.horse == 'horse')
print(Animal.horse.name, Animal.horse.value)

Which gives us:

Animal.horse
True
horse horse


1 Disclosure: I am the author of the Python stdlib Enum, the enum34 backport, and the Advanced Enumeration (aenum) library.

这篇关于将枚举成员的值自动设置为其名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆