用动态成员定义枚举的更 Pythonic 方式

Pet*_*ite 6 python enums python-3.x

我需要创建一个枚举来表示 ISO 国家/地区代码。国家/地区代码数据来自 json 文件,可从以下位置获取:https ://github.com/lukes/ISO-3166-Countries-with-Regional-Codes

所以我所做的是:

data = json.load(open('slim-2.json'))
codes_list = [(data[i]['alpha-2'], int(data[i]['country-code']))
              for i in range(len(data))]

CountryCode = enum.Enum('CountryCode', codes_list,)

names_dict = {int(data[i]['country-code']):data[i]['name'] 
              for i in range(len(data))}
setattr(CountryCode, '_names', names_dict)

CountryCode.choices = classmethod(lambda cls:((member.value, name) 
                                  for name, member in cls.__members__.items()))
setattr(CountryCode, '__str__' ,lambda self: self.__class__._names[self.value])
Run Code Online (Sandbox Code Playgroud)

坦率地说,这段代码片段很丑陋。我研究了定义枚举类的替代方法,但无法拼凑出解决方案。有没有一种方法可以按以下形式定义枚举:

class CountryCode(enum.Enum):

    data = json.load(open('slim-2.json'))
    # Some code to define the enum members

    @classmethod
    def choices(cls):
    # etc...
Run Code Online (Sandbox Code Playgroud)

关于如何执行此操作有什么建议吗?

Eth*_*man 8

更新

在何时应该子类化 EnumMeta 而不是 Enum? 的JSONEnum底部使用 , 你可以这样做:

class Country(JSONEnum):
    _init_ = 'abbr code country_name'  # remove if not using aenum
    _file = 'some_file.json'
    _name = 'alpha-2'
    _value = {
            1: ('alpha-2', None),
            2: ('country-code', lambda c: int(c)),
            3: ('name', None),
            }
Run Code Online (Sandbox Code Playgroud)

原答案

看起来您正在尝试跟踪三项数据:

  • 国家的名字
  • 国家代码
  • 国家/地区 2 字母缩写

您应该考虑使用受 mixin 启发的技术,namedtuple本答案所示


标准库方式

我们需要一个基类来保存行为:

from enum import Enum
import json

class BaseCountry(Enum):

    def __new__(cls, record):
        member = object.__new__(cls)
        member.country_name = record['name']
        member.code = int(record['country-code'])
        member.abbr = record['alpha-2']
        member._value_ = member.abbr, member.code, member.country_name
        if not hasattr(cls, '_choices'):
            cls._choices = {}
        cls._choices[member.code] = member.country_name
        cls._choices[member.abbr] = member.country_name
        return member                

    def __str__(self):
        return self.country_name

    @classmethod
    def choices(cls):
        return cls._choices.copy()
Run Code Online (Sandbox Code Playgroud)

然后我们可以使用它来创建实际的Country类:

Country = BaseCountry(
        'Country',
        [(rec['alpha-2'], rec) for rec in json.load(open('slim-2.json'))],
        )
Run Code Online (Sandbox Code Playgroud)

方式aenum 1 2

from aenum import Enum, MultiValue
import json

class Country(Enum, init='abbr code country_name', settings=MultiValue):

    _ignore_ = 'this country'  # do not add these names as members

    # create members
    this = vars()
    for country in json.load(open('slim-2.json')):
        this[country['alpha-2']] = (
                country['alpha-2'],
                int(country['country-code']),
                country['name'],
                )

    # return a dict of choices by abbr or country code to name
    @classmethod
    def choices(cls):
        mapping = {}
        for member in cls:
            mapping[member.code] = member.name
            mapping[member.abbr] = member.name
        return mapping

    # have str() print just the country name
    def __str__(self):
        return self.country_name
Run Code Online (Sandbox Code Playgroud)

虽然我包含了该choices方法,但您可能不需要它:

>>> Country('AF')
<Country.AF: ('AF', 4, 'Afghanistan')>

>>> Country(4)
<Country.AF: ('AF', 4, 'Afghanistan')>

>>> Country('Afghanistan')
<Country.AF: ('AF', 4, 'Afghanistan')>
Run Code Online (Sandbox Code Playgroud)

1声明:我是Python stdlibEnumenum34backportAdvanced Enumeration ( aenum)库的作者 。

2这需要aenum 2.0.5+.


fal*_*tru 1

这个怎么样?

data = json.load(open('slim-2.json'))
CountryCode = enum.Enum('CountryCode', [
    (x['alpha-2'], int(x['country-code'])) for x in data
])
CountryCode._names = {x['alpha-2']: x['name'] for x in data}
CountryCode.__str__ = lambda self: self._names[self.name]
CountryCode.choices = lambda: ((e.value, e.name) for e in CountryCode)
Run Code Online (Sandbox Code Playgroud)
  • 替换[...data[i]... for i in range(len(data))][...x... for x in data]; 您可以data在不使用索引的情况下迭代序列(列表,在代码中)。
  • 一致使用CountryCode.attr = ...;而不是混合CountryCode.attr = ...setattr(CountryCode, 'attr', ...)