2017-10-16 97 views
1

雖然pylint的提高上enum.Enum(value=..., names=...)警告,我從枚舉DOC看到一個可以以編程方式創建一個枚舉像下面覆蓋枚舉元類初始化

import re 
import enum 
import termios 

def termios_baud_rates(): 
    regexp = r"(?:^|,)B(?P<rate>\d+)" 
    rates = sorted(map(int, re.findall(regexp, ",".join(dir(termios))))) 
    return {"B{:d}".format(r): r for r in rates} 

BAUD_RATES = enum.Enum("BAUD_RATES", termios_baud_rates()) 

但我還想添加方法:

@classmethod 
def valid_rate(cls, value): 
    return (any(value == item.value for item in cls)) 

我認爲這應該涉及重載metaclass __prepare__(mcls, names, bases)以擴大基地的名稱字典,但顯然基地不是如何創建枚舉屬性。任何人有任何提示?

+1

子類枚舉:https://docs.python.org/3/library/enum.html#restricted-subclassing-of-enumerations – slezica

回答

2

它使用aenum library 很簡單:

import re 
import aenum 
import termios 

class BaudRate(aenum.Enum): 
    _ignore_ = 'cls regexp rates' 

    cls = vars() 
    regexp = r"(?:^|,)B(?P<rate>\d+)" 
    rates = sorted(map(int, re.findall(regexp, ",".join(dir(termios))))) 
    for value in rates: 
     cls['B%d' % value] = value 

    @classmethod 
    def valid_rate(cls, value): 
     return (any(value == item.value for item in cls)) 

_ignore_告訴aenum什麼,嗯,忽略,事實上,在_ignore_任何從最終Enum類中刪除。

由於bug in Python's Enum這不工作,除非你使用aenum


披露:我是Python stdlib Enum的作者,enum34 backportAdvanced Enumeration (aenum)庫。

+0

感謝這是我正在尋找的東西。如果你有什麼好的參考鏈接或者如何工作,我會很感激的;我試圖更好地理解metaclass如何在底層工作,並且適用於我當前的項目(例如,在實例化過程中插入特定操作系統代碼的標準化接口/協議類) –

1

如果您改爲創建Enum的新子類,該怎麼辦?

from enum import Enum 

class ValidEnum(Enum): 

    @classmethod 
    def valid_rate(cls, value): 
    return (any(value == item.value for item in cls)) 

或者,根據用例,您可以創建另一個包裝enum的類。

要麼應該工作如下:

In [3]: BAUD_RATES = ValidEnum("BAUD_RATES", termios_baud_rates()) 

In [7]: BAUD_RATES.valid_rate(0) 
Out[7]: True 

In [11]: BAUD_RATES.valid_rate(213) 
Out[11]: False 

希望這有助於!

+1

@Icary謝謝你的例子;我希望只是重寫元類中的'__prepare__'或'__new__',因爲這是需要它們的東西,即改變Enum在到達類__new__或__init__之前的構造方式。 –

+0

更合理!我不確定這個問題入門級的問題,但聽起來像是比簡單的子類複雜一點,在這種情況下,'aenum'庫看起來就像是要走的路。 – lcary

1

您還可以使用nonlocal避免需要的aenum包:

import re 
import enum 
import termios 


regexp = r"(?:^|,)B(?P<rate>\d+)" 
rates = sorted(map(int, re.findall(regexp, ",".join(dir(termios))))) 

value = None 

class BaudRate(enum.Enum):  
    nonlocal value 
    for value in rates: 
     locals()['B%d' % value] = value 

    @classmethod 
    def valid_rate(cls, value): 
     return (any(value == item.value for item in cls))