【问题标题】:Python Enum class (with tostring fromstring)Python Enum 类(带有 tostring fromstring)
【发布时间】:2010-12-17 17:01:32
【问题描述】:

我找到了一种在 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 为中心的方式来完成我上面所做的事情?还是上面已经尽可能简洁了。

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

【问题讨论】:

标签: python enums tostring


【解决方案1】:

[时间流逝...]

新的 Python Enum 终于登陆 3.4 了,has also been backported。所以你的问题的答案是现在使用它。 :)


一个例子:

>>> 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

【讨论】:

    【解决方案2】:

    嗯,这就是你要的:

    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 中使用枚举了,也许你也可以摆脱它们 ;-)

    【讨论】:

    • MyEnum.tostring(3) 返回无。我应该是 MyEnum.tostring(MyEnum.VAL3)
    • 那是因为range 从 0 开始。tostring(2) 给出了VAL3。不幸的名字(*[1-9])。 Afaik,C/C++ enum 也是从 0 开始的。
    • "tostring" 和 "fromsring" 肯定是 javesque。 Python 不需要这个东西。
    • 如果枚举在 Python 中没有任何用处,那么多年来有这么多人觉得需要实现类似的东西,这很奇怪。我个人使用它们的一种方法是给诸如幻数之类的东西命名,并使我的脚本更具描述性和可读性。
    • 奇怪的是有多少人觉得有必要实施它们;他们以某种方式最终进入了标准库!显然,它们有相当的实用性。
    【解决方案3】:

    使用字典:

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

    不需要上课。 dicts 有你的班级,因为 1.) 它们非常高效,2.) 有一堆令人难以置信的方法,3.) 是一种通用的语言结构。它们也是可扩展的:

    MyEnum['VAL4'] = 4
    

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

    如果您想反其道而行之,请构建另一个字典。 (例如{'1':'VAL1', ...}

    【讨论】:

    • 如果我必须构建第二个字典才能走另一条路,这违反了任何语言中首选的单点定义值。您如何回应这种担忧?
    • 可扩展性不是枚举的特性。
    【解决方案4】:

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

    这个很有意思:

    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 中看不到它们的任何用例。枚举在静态类型语言中很有用,因为它们不是动态的;)

    【讨论】:

      【解决方案5】:

      这将做你想做的事并概括你的实现,稍微减少样板代码:

      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
      

      【讨论】:

        【解决方案6】:

        你可以使用字典:

        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 答案肯定更好。但将我的作为幼稚实现的示例。

        【讨论】:

        • 前导双下划线(= name mangling)是......好吧,不是邪恶的,但我从未见过他们有一点好处的假设情况,而不是使用他们只是破坏东西的通常情况.
        • @delnan。我会注意这一点。
        【解决方案7】:

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

        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 本机实现)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-22
          • 1970-01-01
          • 2012-06-06
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多