4

我正在尝试为一个Node类创建一组状态。通常,我会通过将每个Node实例的state变量设置为 an来做到这一点int,并记录哪个 int 对应于哪个状态(因为我没有enums)。
这一次,我想尝试一些不同的东西,所以我决定这样做:

class Node:
  state1 = 1
  state2 = 2

  def __init__(self):
    ...

这很好用。但是,我遇到了一个问题,我有很多状态 - 太多无法手动输入。此外,有这么多状态,我可能会犯错误并将相同的分配int给两个状态。在测试状态时,这将成为错误的来源(例如:如果两者都存在,则if self.state==Node.state1可能会失败)。Node.state1Node.state23

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

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

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

我在 Python3.3.2 上,如果重要的话

4

5 回答 5

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 中工作。

于 2013-11-12T10:17:37.437 回答
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

使用AutoNumberPython 3.4 枚举文档

>>> 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
于 2013-11-12T05:06:24.663 回答
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
于 2013-11-12T05:08:04.440 回答
1

有多种方法可以做到这一点,我想说最明显的一种是使用元类,但是将 for loop 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

或者带有循环的简单版本(但恕我直言丑陋,而且不太灵活):

class Node(object):
    pass

for i, state in enumerate('state1 state2'.split()):
    setattr(Node, state, i)
于 2013-11-12T04:47:29.167 回答
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
于 2014-06-18T04:00:04.563 回答