Python抽象Enum/枚举层级构建:库与消费项目实例化问题
枚举继承实例化问题的简洁解决方案
问题背景
我有一个库项目和多个消费项目,消费项目会调用库的核心功能。库中定义了AbstractProgrammCode枚举作为父类,消费项目需要通过添加枚举值来实现具体的枚举类(比如ProgrammCode)。现在遇到的问题是:库中的通用创建方法无法实例化消费项目里的具体枚举,报错提示440不是AbstractProgrammCode的成员——我知道是因为枚举继承机制没被正确识别,想找最简洁的解决办法。
现有代码示例
库项目中的父枚举
import logging from enum import Enum class AbstractProgrammCode(Enum): @staticmethod def create_with(string: str): if not string or not string.strip().isnumeric(): raise ValueError("'" + str(string) + "' is not a valid Programm Code. Expected: eg. '440'.") result = AbstractProgrammCode(int(string.strip())) logging.getLogger(__name__).debug("ProgrammCode '" + str(result.name) + "' created.") return result
消费项目中的具体枚举
from lib.abstract_programm_code import AbstractProgrammCode class ProgrammCode(AbstractProgrammCode): P440 = 440 P431 = 431 P441 = 441 P455 = 455
调用代码与报错
调用代码:
code = ProgrammCode.create_with(str(440))
报错栈:
tests/re_v1/decision/decider_test.py:100: in __create_dto code = ProgrammCode.create_with(str(programm_nr)) __app__/re_v1/programm/abstract_programm_code.py:11: in create_with result = AbstractProgrammCode(int(string.strip())) /usr/lib/python3.9/enum.py:360: in __call__ return cls.__new__(cls, value) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ cls = <enum 'AbstractProgrammCode'>, value = 440 def __new__(cls, value): # all enum instances are actually created during class construction # without calling this method; this method is called by the metaclass' # __call__ (i.e. Color(3) ), and by pickle if type(value) is cls: # For lookups like Color(Color.RED) return value # by-value search for a matching enum member # see if it's in the reverse mapping (for hashable values) try: return cls._value2member_map_[value] except KeyError: # Not found, no need to do long O(n) search pass except TypeError: # not there, now do long search -- O(n) behavior for member in cls._member_map_.values(): if member._value_ == value: return member # still not found -- try _missing_ hook try: exc = None result = cls._missing_(value) except Exception as e: exc = e result = None try: if isinstance(result, cls): return result else: ve_exc = ValueError("%r is not a valid %s" % (value, cls.__qualname__)) if result is None and exc is None: > raise ve_exc E ValueError: 440 is not a valid AbstractProgrammCode /usr/lib/python3.9/enum.py:678: ValueError
解决方案
问题根源在于create_with是静态方法,里面硬编码实例了父类AbstractProgrammCode,完全没用到调用这个方法的子类(比如ProgrammCode)。把静态方法改成类方法就能解决:
修改后的库枚举代码:
import logging from enum import Enum class AbstractProgrammCode(Enum): @classmethod def create_with(cls, string: str): if not string or not string.strip().isnumeric(): raise ValueError(f"'{string}' is not a valid Programm Code. Expected: eg. '440'.") result = cls(int(string.strip())) # 用cls代替硬编码的AbstractProgrammCode logging.getLogger(__name__).debug(f"ProgrammCode '{result.name}' created.") return result
改动说明
- 把
@staticmethod换成@classmethod,方法参数添加cls——这个cls代表当前调用方法的类(子类ProgrammCode调用时,cls就是ProgrammCode) - 实例枚举时用
cls(int(string.strip()))代替AbstractProgrammCode(...),这样就会去实例子类而非父类 - 顺便把字符串拼接改成f-string,代码更简洁
改完之后再调用ProgrammCode.create_with(str(440)),就能正常返回ProgrammCode.P440了。
内容的提问来源于stack exchange,提问作者Kai B.
相关产品推荐
相关产品推荐

