python - 处理 KeyError 的上下文管理器类
问题描述
我正在尝试实现一个context manager class
处理字典上的 KeyError 的方法。
想想这个:
bikes = ['Honda', 'Yamaha', 'Kawasaki', 'Suzuki']
colors = ['Red', 'Blue', 'Green', 'White', 'Black']
有了这两个列表,我必须构建一个包含每个品牌和颜色的销售额的两层词典,例如:
bike_sales_by_color = {
'Honda': {
'Red': 100,
'Blue': 125,
},
'Yamaha': {
'Black': 50,
'White': 60,
},
# etc...
}
(对于此示例,将销售金额视为随机数)。
我解决这个问题的实现是最普通/常规的:
def get_bikes_sales():
bikes_sales = {}
for bike in bikes: # iterate over two lists
for color in colors:
try: # try to assign a value to the second-level dict.
bikes_sales[bike][color] = 100 # random sales int value
except KeyError: # handle key error if bike is not yet in the first-level dict.
bikes_sales[bike] = {}
bikes_sales[bike][color] = 100 # random sales int value
return bikes_sales
上述函数的行为是预期的,但我希望有一个用户定义的类来节省我们每次必须面对这个问题时重复这段代码,我认为上下文管理器类将是实现它的方法。
这就是我所做的,但它没有按预期工作:
class DynamicDict:
"""
Context manager class that immediately creates a new key with the intended value
and an empty dict as the new created key's value if KeyError is raised.
Useful when trying to build two or more level dictionaries.
"""
def __init__(self):
self.dictionary = {}
def __enter__(self):
return self.dictionary
def __exit__(self, exc_type, exc_val, exc_tb):
if exc_type is KeyError:
self.dictionary[exc_val.args[0]] = {}
return True
这样我们就可以制作类似的东西:
with DynamicDict() as bikes_sales:
for bike in bikes:
for color in colors:
bikes_sales[bike][color] = 100
但是with
块内的迭代在上下文管理器首先处理后停止KeyError
,我得到的结果只有这个:{'Honda': {}}
解决方案
您的实现get_bikes_sales
非常pythonic(尝试/除外)。
使用上下文管理器并不能解决问题 - 只需将其移动到其他地方。
为什么不创建一个动态创建(任意嵌套)字典的函数:
import itertools
import pprint
def generate_nested_dict(*categories, values):
result = {}
# create all combinations of all categories, alternatively recursion could be used.
for tree in (x for x in itertools.product(*categories)):
_dictHandle = result # keeps track of the parent level with in the dict
# Each tree is Honda->Red, Honda->Blue, ...
for i, k in enumerate(tree):
if k not in _dictHandle:
if i < len(tree) - 1:
# add nested dict level
_dictHandle[k] = {}
else:
# nested level exists
if len(values) == 1:
_dictHandle[k] = values[0]
else:
_dictHandle[k] = values.pop(0)
# add value
_dictHandle = _dictHandle[k]
return result
bikes = ['Honda', 'Yamaha', 'Kawasaki', 'Suzuki']
fuels = ['Petrol', 'Diesel', 'Electric', 'Soda']
colors = ['Red', 'Blue', 'Green', 'White', 'Black']
sales = [
(100 * (i + 1)) + (10 * (j + 1)) for i in range(len(bikes))
for j in range(len(colors))
]
# different values
bike_sales_by_color = generate_nested_dict(bikes, colors, values=sales)
pprint.pprint(bike_sales_by_color)
# different values and one category more
bike_sales_by_fuel_and_color = generate_nested_dict(
bikes, fuels, colors, values=[100]
)
pprint.pprint(bike_sales_by_fuel_and_color)
出去:
{'Honda': {'Black': 150, 'Blue': 120, 'Green': 130, 'Red': 110, 'White': 140},
'Kawasaki': {'Black': 350,
'Blue': 320,
'Green': 330,
'Red': 310,
'White': 340},
...
{'Honda': {'Diesel': {'Black': 100,
'Blue': 100,
'Green': 100,
'Red': 100,
'White': 100},
'Electric': {'Black': 100,
'Blue': 100,
'Green': 100,
'Red': 100,
'White': 100},
'Petrol': {'Black': 100,
'Blue': 100,
'Green': 100,
'Red': 100,
'White': 100},
'Soda': {'Black': 100,
'Blue': 100,
'Green': 100,
'Red': 100,
'White': 100}},
...
推荐阅读
- c - 如何将多个字符串作为输入并使用c将它们存储在二维数组中?
- java - 通过 intnet 和 Bundle Java Android Studio 传递值
- vue.js - Billboard JS:悬停在一个图表上,显示所有图表的工具提示
- numpy - 生成分钟的二进制列表,指示所述分钟是否在给定的时间范围内
- javascript - 如何在 Javascript 文件(.js)中使用 Python Flask?
- c# - 对于 .NET 3.5,是否有比具有异步功能的 webclient 更好的方法?
- javascript - 如何使用选择框中的值更改输入框的类型?
- rest - Microsoft Teams Graph API 在未经管理员同意的情况下获取用户所属的团队?
- netsuite - SuiteScript 2.0:将销售订单合二为一
- javascript - 当我为 .clip() 操作传递很长的路径时,画布内容会被截断