When should I subclass EnumMeta instead of Enum?
在本文中,Nick Coghlan讨论了一些涉及到PEP 435
然而,关于使用元类,我给出的建议(我是主要的stdlib
带属性的
Enum 。处理缺少的成员
不是
Enum 成员的类常量
还有一个值得注意的故事,就是在深入研究
- 是否可以重写枚举中的
__new__ 以将字符串解析到实例?
考虑到这些,我什么时候需要摆弄EDOCX1[1]本身呢?
迄今为止,我所看到的最好(也是唯一)的
用动态成员定义枚举的一种更为python的方法
python枚举防止无效的属性分配
我们将在这里进一步研究动态成员案例。
首先,看一下在不将
STDLIB方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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 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 | from aenum import Enum, MultiValue import json class Country(Enum, init='abbr code country_name', settings=MultiValue): _ignore_ = 'country this' # 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'], ) # have str() print just the country name def __str__(self): return self.country_name |
上面的代码对于一次性枚举来说很好——但是如果从JSON文件创建枚举对您来说很常见呢?想象一下,如果你可以这样做:
1 2 3 4 5 6 7 8 9 | 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), } |
正如你所看到的:
_file 是要使用的JSON文件的名称。_name 是该名称应使用的路径。_value 是一个字典,将路径映射到值3_init_ 指定不同值组件的属性名(如果使用aenum )
JSON数据取自https://github.com/lukes/iso-3166-countries-with-regional-codes——以下是一个简短的摘录:
[{"name":"Afghanistan","alpha-2":"AF","country-code":"004"},
{"name":"?land Islands","alpha-2":"AX","country-code":"248"},
{"name":"Albania","alpha-2":"AL","country-code":"008"},
{"name":"Algeria","alpha-2":"DZ","country-code":"012"}]
这是
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | class JSONEnumMeta(EnumMeta): @classmethod def __prepare__(metacls, cls, bases, **kwds): # return a standard dictionary for the initial processing return {} def __init__(cls, *args , **kwds): super(JSONEnumMeta, cls).__init__(*args) def __new__(metacls, cls, bases, clsdict, **kwds): import json members = [] missing = [ name for name in ('_file', '_name', '_value') if name not in clsdict ] if len(missing) in (1, 2): # all three must be present or absent raise TypeError('missing required settings: %r' % (missing, )) if not missing: # process name_spec = clsdict.pop('_name') if not isinstance(name_spec, (tuple, list)): name_spec = (name_spec, ) value_spec = clsdict.pop('_value') file = clsdict.pop('_file') with open(file) as f: json_data = json.load(f) for data in json_data: values = [] name = data[name_spec[0]] for piece in name_spec[1:]: name = name[piece] for order, (value_path, func) in sorted(value_spec.items()): if not isinstance(value_path, (list, tuple)): value_path = (value_path, ) value = data[value_path[0]] for piece in value_path[1:]: value = value[piece] if func is not None: value = func(value) values.append(value) values = tuple(values) members.append( (name, values) ) # get the real EnumDict enum_dict = super(JSONEnumMeta, metacls).__prepare__(cls, bases, **kwds) # transfer the original dict content, _items first items = list(clsdict.items()) items.sort(key=lambda p: (0 if p[0][0] == '_' else 1, p)) for name, value in items: enum_dict[name] = value # add the members for name, value in members: enum_dict[name] = value return super(JSONEnumMeta, metacls).__new__(metacls, cls, bases, enum_dict, **kwds) # for use with both Python 2/3 JSONEnum = JSONEnumMeta('JsonEnum', (Enum, ), {}) |
几点注意事项
JSONEnumMeta.__prepare__ 返回一个正常的dict 。EnumMeta.__prepare__ 用于获取_EnumDict 的实例,这是获取实例的正确方法带前导下划线的键首先传递给实际的
_EnumDict ,因为在处理枚举成员时可能需要这些键。枚举成员的顺序与它们在文件中的顺序相同
1公开:我是python stdlib
2这就需要
3如果您的