2012-10-18 59 views
0

我想在python中創建一個枚舉。我見過幾種解決方案(@alec thomas的第二個回答here對我最感興趣),但是我想讓這個枚舉成爲不變的。我發現一個不可變的python recipe,但我想要一個類似字典的鍵/值關聯。在python中使用duck-punching的不可變枚舉

我試圖使用鴨子衝孔來增加屬性,如果您試圖在該屬性上調用fsetfdel,該屬性將拋出AttributeError

我遇到了麻煩,定義了屬性的fget函數。下面的代碼我到目前爲止:

def enum(*sequential, **named): 
    # Build property dict 
    enums = dict(zip(sequential, range(len(sequential))), **named) 

    # Define an errorhandler function 
    def err_func(*args, **kwargs): 
     raise AttributeError('Enumeration is immutable!') 

    # Create a base type 
    t = type('enum', (object,), {}) 

    # Add properties to class by duck-punching 
    for attr, val in enums.iteritems(): 
     setattr(t, attr, property(lambda attr: enums[attr], err_func, err_func)) 

    # Return an instance of the new class 
    return t() 

e = enum('OK', 'CANCEL', 'QUIT') 
print e 
print e.OK 
print e.CANCEL 
print e.QUIT 

# Immutable? 
e.OK = 'ASDF' # Does throw the correct exception 
print e.OK 

從這個輸出是:

<__main__.enum object at 0x01FC8F70> 
Traceback (most recent call last): 
    File "enum.py", line 24, in <module> 
    print e.OK 
    File "enum.py", line 17, in <lambda> 
    setattr(t, attr, property(lambda attr: enums[attr], err_func, err_func)) 
KeyError: <__main__.enum object at 0x01FC8F70> 

也許這是不是創建一個枚舉的最佳方式,但它的短,我想探索更多的這整個鴨子打孔/猴子修補的概念。

回答

1

您的直接問題是propertygetter需要self作爲唯一參數,而不是attr。因此,您應該使用類似lambda self: val的內容。

但是,這是行不通的,因爲lambda綁定名稱val,它從迭代更改爲迭代。所以你需要以某種方式包裝它:

def getter(val): 
    return lambda self: val 

for attr, val in enums.iteritems(): 
    setattr(t, attr, property(getter(val), err_func, err_func)) 
+0

謝謝刪除或修改!最終的解決方案如下所示:'def getter(cls,val):return lambda cls:val'。這些屬性是這樣插入的:'setattr(t,attr,property(getter(t,val),err_func,err_func))' – Josh

0

最終實現(感謝@ nneonneo)。

  • 檢查重複枚舉密鑰
  • 檢查是否枚舉爲空
  • 不允許枚舉項目

    def enum(*sequential, **named): 
        # Check for duplicate keys 
        names = list(sequential) 
        names.extend(named.keys()) 
        if len(set(names)) != len(names): 
         raise KeyError('Cannot create enumeration with duplicate keys!') 
    
        # Build property dict 
        enums = dict(zip(sequential, range(len(sequential))), **named) 
        if not enums: 
         raise KeyError('Cannot create empty enumeration') 
    
        # Function to be called as fset/fdel 
        def err_func(*args, **kwargs): 
         raise AttributeError('Enumeration is immutable!') 
    
        # function to be called as fget 
        def getter(cls, val): 
         return lambda cls: val 
    
        # Create a base type 
        t = type('enum', (object,), {}) 
    
        # Add properties to class by duck-punching 
        for attr, val in enums.iteritems(): 
         setattr(t, attr, property(getter(t, val), err_func, err_func)) 
    
        # Return an instance of the new class 
        return t()