覆盖枚举元类初始化

问题描述:

虽然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

它使用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如何在底层工作,并且适用于我当前的项目(例如,在实例化过程中插入特定操作系统代码的标准化接口/协议类) –

如果您改为创建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

您还可以使用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))