2014-08-27 67 views
1

類正確枚舉值我previous question被問及枚舉蟒蛇的Python元類來生成

比方說,我創建國家類:

class State(object): 
    """ 
    Abstract class representing a state. 
    """ 
    enum = 1 

    def __init__(self, instance): 
     self.instance = instance 

    def is_flag(self, flag): 
     return flag & self.enum == self.enum 

然後我創建不同的實際狀態

class New(State): 
    enum = 2 
    def pay(self): 
     ... 

class Paid(State): 
    enum = 4 
    def complete(self): 
     ... 

class Completed(State): 
    enum = 8 


ACCOUNTABLE = Paid.enum | Completed.enum 

雖然這個工作,我想自動生成的枚舉值,它似乎可以通過使用Meta類來完成,問題是如何?

+0

抽象基類不是元類的同一個事物。無論如何,使用一個包含單個整數值的屬性創建單獨的類似乎是創建Enum值非常沉重的方法。這種方法的另一個問題是,生成的整數值可能會根據定義的子類的順序和數量而變化 - 這意味着它們不會是常量值,這對於枚舉通常是可取的。 – martineau 2014-08-28 04:10:23

+0

我也認爲可以通過元類生成不一致的值。我想實現StateMachine模式,而不僅僅是Enum。 – 2014-08-28 20:48:23

+0

目前尚不清楚StateMachine模式如何能夠避免不一致的值的問題。此外,在您發佈自己的問題的答案中,您也不需要使用ABCMeta。在Python中,抽象基類與C++中的不同。如果你堅持用類來表示不同的枚舉值 - 一個可疑的方法 - 一個元類可能是你所需要的。 – martineau 2014-08-28 21:02:04

回答

1

雖然我不不認爲爲每個枚舉值定義一個單獨的類是a由於我評論中陳述的原因非常穩健的方法,這裏有一種方法可以完成,這將支持創建多個獨立的基礎狀態類。

class MetaState(type): 
    _baseclasses = {} # registry of instances & number of subclasses of each 
    def __new__(cls, name, bases, attrs): 
     cls = super(MetaState, cls).__new__(cls, name, bases, attrs) 
     if bases == (object,): # definition of a base class? 
      MetaState._baseclasses[cls] = 0 # create initial registry entry 
     else: # must be derived from a previously defined base state class 
      for base in bases: # find base state class 
       if base in MetaState._baseclasses: 
        setattr(cls, 'enum', 2 ** MetaState._baseclasses[base]) 
        MetaState._baseclasses[base] += 1 
        break 
      else: 
       raise TypeError('Class not derived from base state class') 
     return cls 

class BaseState(object): 
    """ Abstract base class for each derived state subclass. """ 
    __metaclass__ = MetaState 

    def is_flag(self, flag): 
     return flag & self.enum == self.enum 

class A(BaseState): pass 
class B(BaseState): pass 
class C(BaseState): pass 

print A.enum # -> 1 
print B.enum # -> 2 
print C.enum # -> 4 
3

Python 3.4有一個Enum data type,其中has been backported

from enum import IntEnum 

States = IntEnum('States', [(n, 2**i) for i, n in enumerate('New Paid Complete'.split(), 1)]) 

list(States) # [<States.New: 2>, <States.Paid: 4>, <States.Complete: 8>] 

class States(IntEnum): 
    New = 2 
    Paid = 4 
    Complete = 8 
    def is_flag(self, flag): 
     return self & flag == flag 
0

我還拿出一個解決方案(需要修理),我的回答:

class StateMeta(type): 

    def __new__(cls, name, bases, attrs): 
     cls = super(StateMeta, cls).__new__(cls, name, bases, attrs) 
     if bases[-1] == object: 
      return cls 

     setattr(cls, 'enum', 2 ** (len(bases[-1].__subclasses__())-1)) 
     return cls 


class State(object): 
    """ 
    Abstract class representing a state. 
    """ 
    __metaclass__ = StateMeta 

    enum = 0 

    def __init__(self, instance): 
     self.instance = instance 

    def is_flag(self, flag): 
     return flag & self.enum == self.enum 

運行它:

>>> class A(State): 
...  pass 
... 
>>> A.enum 
1 
>>> class B(State): 
...  pass 
... 
>>> B.enum 
2 
+0

爲什麼跳過1(2 ** 0)的枚舉值? – martineau 2014-08-28 21:05:02

+0

我沒有跳過目的,但我無法使用迭代器值的功能,因爲它在單個類作用域中生成枚舉值。 – 2014-08-28 22:00:25

+0

當定義類State時,以及當類A和B是(因爲後兩個繼承State的元類)時調用'StateMeta .__ new __()',所以我沒有明白你的意思是「單一課堂範圍」。 – martineau 2014-08-28 22:41:06