A more pythonic way to define an enum with dynamic members
我需要创建一个枚举来表示ISO国家代码。国家代码数据来自JSON文件,可从以下网址获取:https://github.com/lukes/iso-3166-countries-with-regional-codes
所以我所做的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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]) |
坦率地说,这段代码很难看。我研究了定义枚举类的其他方法,但无法将解决方案组合在一起。是否可以用以下形式定义枚举:
1 2 3 4 5 6 7 8 | class CountryCode(enum.Enum): data = json.load(open('slim-2.json')) # Some code to define the enum members @classmethod def choices(cls): # etc... |
号
有什么建议吗?
通用
看起来您试图跟踪三个数据:
- 国家名称
- 国家代码
- 国家2字母缩写
- 用
[...x... for x in data] 替换[...data[i]... for i in range(len(data))] ;不使用索引就可以生成一个序列(列表,代码中的data )。 - 一直使用
CountryCode.attr = ... ;而不是混合CountryCode.attr = ... 和setattr(CountryCode, 'attr', ...) 。
小精灵
您应该考虑使用受
stdlib方式
我们需要一个基类来保存该行为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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() |
然后我们可以使用它来创建实际的
1 2 3 4 | Country = BaseCountry( 'Country', [(rec['alpha-2'], rec) for rec in json.load(open('slim-2.json'))], ) |
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | 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 |
虽然我包含了
1 2 3 4 5 6 7 8 | >>> Country('AF') <Country.AF: ('AF', 4, 'Afghanistan')> >>> Country(4) <Country.AF: ('AF', 4, 'Afghanistan')> >>> Country('Afghanistan') <Country.AF: ('AF', 4, 'Afghanistan')> |
。
1公开:我是python stdlib
2这就需要
是的,有一种方法可以使用您想要的替代声明语法来定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import enum import json class CountryCodeMeta(enum.EnumMeta): def __new__(metacls, cls, bases, classdict): data = classdict['data'] names = [(country['alpha-2'], int(country['country-code'])) for country in data] temp = type(classdict)() for name, value in names: temp[name] = value excluded = set(temp) | set(('data',)) temp.update(item for item in classdict.items() if item[0] not in excluded) return super(CountryCodeMeta, metacls).__new__(metacls, cls, bases, temp) class CountryCode(enum.Enum, metaclass=CountryCodeMeta): data = json.load(open('slim-2.json')) @classmethod def choices(cls): return ((member.value, name) for name, member in cls.__members__.items()) |
号
这个怎么样?
1 2 3 4 5 6 7 | 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) |
小精灵