Python Enum 类(带有 tostring fromstring)

发布于 2024-10-08 00:22:56 字数 907 浏览 1 评论 0原文

我找到了一种简单的方法来实现(破解)Python 中的枚举:

class MyEnum:
  VAL1, VAL2, VAL3 = range(3)

然后我可以这样称呼它:

bob = MyEnum.VAL1

性感!

好吧,现在我希望能够在给定字符串的情况下获取数值,或者在给定数值的情况下获取字符串。假设我希望字符串与 Enum 键完全匹配

我能想到的最好的方法是这样的:

class MyEnum:
  VAL1, VAL2, VAL3 = range(3)
  @classmethod
  def tostring(cls, val):
    if (val == cls.VAL1):
      return "VAL1"
    elif (val == cls.VAL2):
      return "VAL2"
    elif (val == cls.VAL3):
      return "VAL3"
    else:
      return None
  @classmethod
  def fromstring(cls, str):
    if (str.upper() == "VAL1"):
      return cls.VAL1
    elif (str.upper() == "VAL2"):
      return cls.VAL2
    elif (str.upper() == "VAL2"):
      return cls.VAL2
    else:
      return None

或类似的东西(忽略我如何捕获无效的情况)

是否有更好的、更以 python 为中心的方法我上面在做什么?或者上面的内容已经尽可能简洁了。

看来必须有更好的方法来做到这一点。

I've found a simply way to implement(hack) an enum into Python:

class MyEnum:
  VAL1, VAL2, VAL3 = range(3)

I can then call this as such:

bob = MyEnum.VAL1

Sexy!

Alright, now I want to be able to get both the numerical value if given a string, or a string if given a numerical value. Let's say I want the strings to exactly match up to the Enum key's

The best I could think of is something like this:

class MyEnum:
  VAL1, VAL2, VAL3 = range(3)
  @classmethod
  def tostring(cls, val):
    if (val == cls.VAL1):
      return "VAL1"
    elif (val == cls.VAL2):
      return "VAL2"
    elif (val == cls.VAL3):
      return "VAL3"
    else:
      return None
  @classmethod
  def fromstring(cls, str):
    if (str.upper() == "VAL1"):
      return cls.VAL1
    elif (str.upper() == "VAL2"):
      return cls.VAL2
    elif (str.upper() == "VAL2"):
      return cls.VAL2
    else:
      return None

or something like that (ignore how i'm catching invalid cases)

Is there a better, more python centric way to do what I'm doing above? Or is the above already as concise as it gets.

It seems like there's got to be a better way to do it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

春花秋月 2024-10-15 00:22:56

[时间流逝...]

新的Python Enum终于登陆3.4,并且也已向后移植。所以你的问题的答案现在就是使用它。 :)


一个例子:

>>> from enum import Enum
>>> class Modes(Enum) :
...    Mode1 = "M1"
...    Mode2 = "M2"
...    Mode3 = "M3"
...

>>> Modes.Mode1
<Modes.Mode1: 'M1'>

>>> Modes.Mode1.value
'M1'

>>> Modes.Mode1.value
'M1'

>>> Modes['Mode1']    # index/key notation for name lookup
<Modes.Mode1: 'M1'>

>>> Modes('M1')       # call notation for value lookup
<Modes.Mode1: 'M1'>

>>> Modes("XXX")      # example error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Anaconda3\lib\enum.py", line 291, in __call__
    return cls.__new__(cls, value)
  File "C:\Anaconda3\lib\enum.py", line 533, in __new__
    return cls._missing_(value)
  File "C:\Anaconda3\lib\enum.py", line 546, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 'XXX' is not a valid Modes

[Time passes...]

The new Python Enum has finally landed in 3.4, and has also been backported. So the answer to your question is now to use that. :)


An example:

>>> from enum import Enum
>>> class Modes(Enum) :
...    Mode1 = "M1"
...    Mode2 = "M2"
...    Mode3 = "M3"
...

>>> Modes.Mode1
<Modes.Mode1: 'M1'>

>>> Modes.Mode1.value
'M1'

>>> Modes.Mode1.value
'M1'

>>> Modes['Mode1']    # index/key notation for name lookup
<Modes.Mode1: 'M1'>

>>> Modes('M1')       # call notation for value lookup
<Modes.Mode1: 'M1'>

>>> Modes("XXX")      # example error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Anaconda3\lib\enum.py", line 291, in __call__
    return cls.__new__(cls, value)
  File "C:\Anaconda3\lib\enum.py", line 533, in __new__
    return cls._missing_(value)
  File "C:\Anaconda3\lib\enum.py", line 546, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 'XXX' is not a valid Modes
执妄 2024-10-15 00:22:56

好吧,这就是你所要求的:

class MyEnum:
  VAL1, VAL2, VAL3 = range(3)
  @classmethod
  def tostring(cls, val):
    for k,v in vars(cls).iteritems():
        if v==val:
            return k

  @classmethod
  def fromstring(cls, str):
      return getattr(cls, str.upper(), None)

print MyEnum.fromstring('Val1')
print MyEnum.tostring(2)

但我真的不明白 Python 中枚举的意义。它拥有如此丰富的类型系统以及用于管理状态的生成器和协程。

我知道我已经超过 12 年没有在 Python 中使用枚举了,也许你也可以摆脱它们;-)

Well, here is what you asked for:

class MyEnum:
  VAL1, VAL2, VAL3 = range(3)
  @classmethod
  def tostring(cls, val):
    for k,v in vars(cls).iteritems():
        if v==val:
            return k

  @classmethod
  def fromstring(cls, str):
      return getattr(cls, str.upper(), None)

print MyEnum.fromstring('Val1')
print MyEnum.tostring(2)

But I really don't get the point of Enums in Python. It has such a rich type system as well as generators and coroutines to manage states.

I know I've not been using Enums in Python for more than 12 years, maybe you can get rid of them too ;-)

灼疼热情 2024-10-15 00:22:56

使用字典:

MyEnum = {'VAL1': 1, 'VAL2':2, 'VAL3':3}

无需上课。字典之所以能打败你的班级,是因为 1.) 它们非常高效,2.) 内置了一堆令人难以置信的方法,3.) 是一种通用语言结构。它们也是可扩展的:

MyEnum['VAL4'] = 4

在 Python 中实现 C++(或其他语言)的功能并不明智。如果你发现自己“破解了一个枚举”或类似的东西,你可以打赌你不是用 Python 的方式做的。

如果你想走相反的路,那就建立另一个字典。 (例如 {'1':'VAL1', ...}

Use a dict:

MyEnum = {'VAL1': 1, 'VAL2':2, 'VAL3':3}

No classes necessary. Dicts have your class beat because 1.) they're incredibly efficient, 2.) have a bunch of incredible methods baked in, and 3.) are a universal language construct. They're also extensible:

MyEnum['VAL4'] = 4

It's not wise to implement C++ (or another language's) functionality in Python. If you find yourself "hacking up an enum" or something of that nature, you can bet the farm you're not doing it the Python way.

If you want to go the opposite way, build another dict. (e.g. {'1':'VAL1', ...}

过期情话 2024-10-15 00:22:56

看:
如何在 Python 中表示“枚举”?< /a>

这个很有趣:

class EnumMeta(type):
  def __getattr__(self, name):
    return self.values.index(name)

  def __setattr__(self, name, value):  # this makes it read-only
    raise NotImplementedError

  def __str__(self):
    args = {'name':self.__name__, 'values':', '.join(self.values)}
    return '{name}({values})'.format(**args)

  def to_str(self, index):
    return self.values[index]

class Animal(object):
  __metaclass__ = EnumMeta
  values = ['Horse','Dog','Cat']

使用:

In [1]: Animal.to_str(Animal.Dog)
Out[1]: 'Dog'
In [2]: Animal.Dog
Out[2]: 1
In [3]: str(Animal)
Out[3]: 'Animal(Horse, Dog, Cat)'

它简单且轻量。这种方法有什么缺点吗?

编辑:
AFAIK 枚举作为一个概念并不是很Pythonic,这就是为什么它们一开始就没有被实现。我从未使用过它们,也看不到它们在 Python 中的任何用例。枚举在静态类型语言中很有用,因为它们不是动态的;)

See:
How can I represent an 'Enum' in Python?

This one is interesting:

class EnumMeta(type):
  def __getattr__(self, name):
    return self.values.index(name)

  def __setattr__(self, name, value):  # this makes it read-only
    raise NotImplementedError

  def __str__(self):
    args = {'name':self.__name__, 'values':', '.join(self.values)}
    return '{name}({values})'.format(**args)

  def to_str(self, index):
    return self.values[index]

class Animal(object):
  __metaclass__ = EnumMeta
  values = ['Horse','Dog','Cat']

Use:

In [1]: Animal.to_str(Animal.Dog)
Out[1]: 'Dog'
In [2]: Animal.Dog
Out[2]: 1
In [3]: str(Animal)
Out[3]: 'Animal(Horse, Dog, Cat)'

It's simple and lightweight. Are they any disadvantages of this approach?

EDIT:
AFAIK enums are not very pythonic as a concept, thats why they were not implemented in the first place. I never used them, and can't see any usecase for them in Python. Enums are useful in static typed languages, because they are not dynamic ;)

心的位置 2024-10-15 00:22:56

这将执行您想要的操作并概括您的实现,稍微减少样板代码:

class EnumBase: # base class of all Enums
    @classmethod
    def tostring(cls, value):
        return dict((v,k) for k,v in cls.__dict__.iteritems())[value]

    @classmethod
    def fromstring(cls, name):
        return cls.__dict__[name]

class MyEnum(EnumBase): VAL1, VAL2, VAL3 = range(3)

print MyEnum.fromstring('VAL1')
# 0
print MyEnum.tostring(1)
# VAL2

This will do what you want and generalizes your implementation slightly reducing boiler-plate code:

class EnumBase: # base class of all Enums
    @classmethod
    def tostring(cls, value):
        return dict((v,k) for k,v in cls.__dict__.iteritems())[value]

    @classmethod
    def fromstring(cls, name):
        return cls.__dict__[name]

class MyEnum(EnumBase): VAL1, VAL2, VAL3 = range(3)

print MyEnum.fromstring('VAL1')
# 0
print MyEnum.tostring(1)
# VAL2
枕头说它不想醒 2024-10-15 00:22:56

您可以使用字典:

class MyEnum:
    VAL1, VAL2, VAL3 = range(3)
    __toString = { VAL1 : "VAL1", VAL2 : "VAL2", VAL3 : "VAL3" }

    @classmethod
    def tostring(cls, val):
        return cls.__toString.get(val)

    @classmethod
    def fromstring(cls, str):
        i = str.upper()
        for k,v in cls.__toString.iteritems():
            if v == i:
                return k
        return None


print MyEnum.tostring(MyEnum.VAL1)
print MyEnum.fromstring("VAL1")

编辑:THC4k 答案肯定更好。但留下我的作为幼稚实施的例子。

You could use dictionaries:

class MyEnum:
    VAL1, VAL2, VAL3 = range(3)
    __toString = { VAL1 : "VAL1", VAL2 : "VAL2", VAL3 : "VAL3" }

    @classmethod
    def tostring(cls, val):
        return cls.__toString.get(val)

    @classmethod
    def fromstring(cls, str):
        i = str.upper()
        for k,v in cls.__toString.iteritems():
            if v == i:
                return k
        return None


print MyEnum.tostring(MyEnum.VAL1)
print MyEnum.fromstring("VAL1")

Edit : THC4k answers is definitely better. But left mine as an example of naive implementation.

南风几经秋 2024-10-15 00:22:56

您不必在类中硬编码您的值 - 您最好有一个枚举器工厂。
同时,只需添加Python提供的一些好处,例如,覆盖表示方法,或属性获取:

class Enumerator(object):
    def __init__(self, *names):
        self._values = dict((value, index) for index, value in enumerate (names))
    def __getattribute__(self, attr):
        try:
            return object.__getattribute__(self,"_values")[attr]
        except KeyError:
            return object.__getattribute__(self, attr)
    def __getitem__(self, item):
        if isinstance (item, int):
            return self._values.keys()[self._values.values().index(item)]
        return self._values[item]
    def __repr__(self):
        return repr(self._values.keys())

现在只需使用它:(

>>> enum = Enumerator("val1", "val2", "val3")
>>> enum
['val3', 'val2', 'val1']
>>> enum.val2
1
>>> enum["val1"]
0
>>> enum[2]
'val3'

顺便说一句,Python开发人员列表中的人正在谈论这个,很可能我们会
有一个更完整且具有足够功能的 Python 3.3 本地实现)

You should not have to hardcode your values inside the class - you better have an enumerator factory.
WHile at that, just add some nicetirs provided by Python, for example, override the represntation method, or attribute getting:

class Enumerator(object):
    def __init__(self, *names):
        self._values = dict((value, index) for index, value in enumerate (names))
    def __getattribute__(self, attr):
        try:
            return object.__getattribute__(self,"_values")[attr]
        except KeyError:
            return object.__getattribute__(self, attr)
    def __getitem__(self, item):
        if isinstance (item, int):
            return self._values.keys()[self._values.values().index(item)]
        return self._values[item]
    def __repr__(self):
        return repr(self._values.keys())

Now just use that:

>>> enum = Enumerator("val1", "val2", "val3")
>>> enum
['val3', 'val2', 'val1']
>>> enum.val2
1
>>> enum["val1"]
0
>>> enum[2]
'val3'

(btw, people in the Python developers list are talking about this,most likely we will
have a more complete, and with enough features, implementation of this natively by Python 3.3)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文