动态创建静态变量(Enum hack)

时间:2013-11-12 04:43:32

标签: python python-3.x enums python-3.3 class-variables

我正在尝试为Node类创建一组状态。通常,我会通过将每个Node实例的state变量设置为int来执行此操作,并记录哪个int对应于哪个状态(因为我没有enum s )。
这一次,我想尝试不同的东西,所以我决定采用这个:

class Node:
  state1 = 1
  state2 = 2

  def __init__(self):
    ...

这很有效。但是,我遇到了一个问题,我有很多状态 - 手动输出太多了。此外,在许多状态下,我可能会出错并将相同的int分配给两个状态。在测试状态时,这将成为错误的来源(例如:if self.state==Node.state1可能会失败,如果Node.state1Node.state2都是3)。

出于这个原因,我想做这样的事情:

class Node:
  def __init__(self):
    ...
...

for i,state in enumerate("state1 state2".split()):
  setattr(Node, state, i)

虽然这可以解决将值分配给状态时的人为错误,但它非常难看,因为类变量是在类定义之外设置的。

有没有办法以这种方式在类定义中设置类变量?我最好这样做:

class Node:
  for i,state in enumerate("state1 state2".split()):
    setattr(Node, state, i)

...但由于Node尚未定义,因此无效,并且会产生NameError

或者,python3.3中是否存在enum

我正在使用Python3.3.2,如果重要的话

5 个答案:

答案 0 :(得分:3)

现在Python有Enum类型,没有理由不使用它。有这么多州,我建议使用有记录的AutoEnum。像这样:

class Node:

    class State(AutoEnum):
        state1 = "initial state before blah"
        state2 = "after frobbing the glitz"
        state3 = "still needs the spam"
        state4 = "now good to go"
        state5 = "gone and went"

    vars().update(State.__members__)

然后在使用中:

--> Node.state2
<State.state2: 2>

注意:链接的配方适用于Python 2.x - 您需要删除unicode引用才能使其在Python 3.x中运行。

答案 1 :(得分:2)

enum34: Python 3.4 Enum backported

>>> import enum
>>> State = enum.IntEnum('State', 'state1 state2')
>>> State.state1
<State.state1: 1>
>>> State.state2
<State.state2: 2>
>>> int(State.state2)
2

使用AutoNumber from Python 3.4 enum documentation

>>> import enum
>>> class AutoNumber(enum.Enum):
...     def __new__(cls):
...         value = len(cls.__members__) + 1
...         obj = object.__new__(cls)
...         obj._value_ = value
...         return obj
... 
>>> class Node(AutoNumber):
...     state1 = ()
...     state2 = ()
... 
>>> Node.state1
<Node.state1: 1>
>>> Node.state2
<Node.state2: 2>
>>> Node.state2.value
2

答案 2 :(得分:2)

如果您在课程定义之后执行setattr的唯一问题是它丑陋而且位置错误,那么使用装饰器来做这件事呢?

def add_constants(names):
    def adder(cls):
        for i, name in enumerate(names):
            setattr(cls, name, i)
        return cls
    return adder

@add_constants("state1 state2".split())
class Node:
    pass

答案 3 :(得分:1)

有多种方法可以做到这一点,我想说最明显的一种方法是使用元类,但移动你的for循环1缩进级别也会有效。

关于枚举的存在:http://docs.python.org/3.4/library/enum.html

这是一个元类示例:

class EnumMeta(type):
    def __new__(cls, name, bases, dict_):
        names = dict_.get('_enum_string', '')
        if names:
            for i, name in enumerate(names.split()):
                dict_[name] = 'foo %d' % i

        return super(EnumMeta, cls).__new__(cls, name, bases, dict_)


class Node(object):
    __metaclass__ = EnumMeta
    _enum_string = 'state1 state2'

print 'state1', SomeMeta.state1
print 'state2', SomeMeta.state2

或带有循环的简单版本(但丑陋的imho,而且不太灵活):

class Node(object):
    pass

for i, state in enumerate('state1 state2'.split()):
    setattr(Node, state, i)

答案 4 :(得分:1)

  

有没有办法以这种方式在类定义中设置类变量?我最好这样做:

class Node:
    for i,state in enumerate("state1 state2".split()):
        setattr(Node, state, i)
  

...但由于Node尚未定义,因此无法正常工作,并且会导致NameError

虽然该类尚不存在,但它所使用的命名空间仍然存在。可以使用vars()(我认为locals())访问它。这意味着您可以执行以下操作:

class Node:
    node_namespace = vars()
    for i, state in enumerate('state1 state2'.split()):
        node_namespace[state] = i
    del node_namespace