使用Python 3.6&枚举( 注意枚举是v3.6的新增功能 )
我试图弄清楚是否有一个" automagic"通过字符串引用字典的Enum Flags的赋值方式。在我的例子中,一个JSON配置文件加载了指定Enum Flags的文本,但我必须执行一堆" if"将节点重新分配给实际枚举的语句。
下面的示例可以正常工作,但是当您对udpate有很多引用时,它会变得很糟糕。
配置文件较大,包含文本,数字和枚举。
示例代码
#import json
from enum import Enum, Flag
class stream_data(Flag):
wind= (0x00000001)
humidity = (0x00000002)
#with open('strings.json') as json_file:
# config = json.load(json_file)
# Assume we loaded with json.load
config = {
"options": {
"stream": {
"capture_rate": "1", "stream_data": "wind", "stream_pull_rate": 5, "stream_type": "binary"
}
}
}
print('type before: %s' % type(config['options']['stream']['stream_data']))
if config['options']['stream']['stream_data'] == stream_data.wind.name:
config['options']['stream']['stream_data'] = stream_data.wind
print('type after: %s' % type(config['options']['stream']['stream_data']))
结果:
type before: <class 'str'>
type after: <enum 'stream_data'>
有什么蟒蛇魔法,我不知道吗?
(我以为我可以遍历dict和enum类来查看名称是否匹配,但这看起来有点像kludgy。)
答案 0 :(得分:2)
枚举会公开其内部dict
,以便您可以使用以下命令按名称访问其值:
stream_data["wind"] # 1
stream_data["humidity"] # 2
# etc.
因此,在您设置config
(c
以适合而不滚动)的情况下,您可以自动替换为:
c['options']['stream']['stream_data'] = stream_data[c['options']['stream']['stream_data']]
UPDATE :如果要自动化所有内容,可以编写一个小的递归函数来遍历您的结构,并尝试替换与特定Enum
名称匹配的所有键的值。全局范围,例如:
import collections
import enum
def decode_enums(target, scope=None): # a simple recursive enum value replacer
if isinstance(target, collections.MutableMapping): # check if dict-like...
scope = scope or globals() # if a scope dict is not passed, use globals()
for k, v in target.items(): # iterate over all the items in the passed dictionary
if k in scope and issubclass(scope[k], enum.Enum): # enum found in the scope
try:
target[k] = scope[k][v] # replace with the 'enum' value
continue
except KeyError: # the value is not enumerated, fall-back to decoding
pass
target[k] = decode_enums(v, scope) # dig deeper...
elif hasattr(target, "__iter__") and not isinstance(target, (str, bytes, bytearray)):
for v in target: # iterate over the elements of this iterable
decode_enums(v) # try to decode their values
return target # return the passed value to enable recursive assignment
现在,您可以按照以下内容定义您的心灵内容:
import enum
class stream_data(enum.Flag):
wind = 0x00000001
humidity = 0x00000002
class stream_type(enum.Flag): # just for the kick of it
binary = 0x00000001
text = 0x00000002
然后使用实际的Enum
值自动更新您的结构:
def value_info(prefix, name, value): # a small function to trace the value info
type_ = type(value)
print(f'{prefix:<6} - {name}: {value:>30}')
print(f'{prefix:<6} - type({name}): {type_!s:>24}')
config = {
"options": {
"stream": {
"capture_rate": "1", "stream_data": "wind",
"stream_pull_rate": 5, "stream_type": "binary"
}
}
}
value_info("before", "stream_data", config["options"]["stream"]["stream_data"])
value_info("before", "stream_type", config["options"]["stream"]["stream_type"])
decode_enums(config)
value_info("after", "stream_data", config["options"]["stream"]["stream_data"])
value_info("after", "stream_type", config["options"]["stream"]["stream_type"])
哪个会给你:
before - stream_data: wind
before - type(stream_data): <class 'str'>
before - stream_type: binary
before - type(stream_type): <class 'str'>
after - stream_data: stream_data.wind
after - type(stream_data): <enum 'stream_data'>
after - stream_type: stream_type.binary
after - type(stream_type): <enum 'stream_type'>
您甚至可以通过为您的枚举定义特定地图来取消globals()
的范围:
class StreamData(enum.Flag):
wind = 0x00000001
humidity = 0x00000002
value_info("before", "stream_data", config["options"]["stream"]["stream_data"])
value_info("before", "stream_type", config["options"]["stream"]["stream_type"])
decode_enums(config, {"stream_data": StreamData})
value_info("after", "stream_data", config["options"]["stream"]["stream_data"])
value_info("after", "stream_type", config["options"]["stream"]["stream_type"])
将产生:
before - stream_data: wind
before - type(stream_data): <class 'str'>
before - stream_type: binary
before - type(stream_type): <class 'str'>
after - stream_data: StreamData.wind
after - type(stream_data): <enum 'StreamData'>
after - stream_type: binary
after - type(stream_type): <class 'str'>
您还可以创建一个对应的encode_enums()
函数,以便在存储将执行相同递归遍历的JSON时使用,但不是将枚举与其值相关联,而是将值转回名称。
所有这一切都说,鉴于您丢失了重要的类型信息,我建议您转移到YAML format。它允许您创建类型扩展,以便您可以将枚举元数据保留在YAML结构中,并且您不必在解析后遍历整个结构,只是为了尝试猜测枚举值。