Python 在列表理解中引发错误(或更好的选择)
Posted
技术标签:
【中文标题】Python 在列表理解中引发错误(或更好的选择)【英文标题】:Python Raising Errors within List Comprehension (or a better alternative) 【发布时间】:2019-06-29 02:03:23 【问题描述】:我有一个从 json 字符串读取的嵌套结构,看起来类似于以下内容...
[
"id": 1,
"type": "test",
"sub_types": [
"id": "a",
"type": "sub-test",
"name": "test1"
,
"id": "b",
"name": "test2",
"key_value_pairs": [
"key": 0,
"value": "Zero"
,
"key": 1,
"value": "One"
]
]
]
我需要提取和旋转数据,准备好插入数据库...
[
(1, "b", 0, "Zero"),
(1, "b", 1, "One")
]
我正在做以下事情......
data_list = [
(
type['id'],
sub_type['id'],
key_value_pair['key'],
key_value_pair['value']
)
for type in my_parsed_json_array
if 'sub_types' in type
for sub_type in type['sub_types']
if 'key_value_pairs' in sub_type
for key_value_pair in sub_type['key_value_pairs']
]
到目前为止,一切都很好。
不过,接下来我需要做的是强制实施一些约束。比如……
if type['type'] == 'test': raise ValueError('[test] types can not contain key_value_pairs.')
但我无法理解。而且我不想诉诸循环。到目前为止,我最好的想法是......
def make_row(type, sub_type, key_value_pair):
if type['type'] == 'test': raise ValueError('sub-types of a [test] type can not contain key_value_pairs.')
return (
type['id'],
sub_type['id'],
key_value_pair['key'],
key_value_pair['value']
)
data_list = [
make_row(
type,
sub_type,
key_value_pair
)
for type in my_parsed_json_array
if 'sub_types' in type
for sub_type in type['sub_types']
if 'key_value_pairs' in sub_type
for key_value_pair in sub_type['key_value_pairs']
]
这可行,但它会检查每个 key_value_pair,这感觉是多余的。 (每组键值对可能有数千对,只需要检查一次就可以知道它们都没有问题。)
此外,还有其他类似的规则适用于层次结构的不同级别。比如“test”类型只能包含“sub_test”子类型。
除了上述之外还有哪些选择?
更优雅? 更可扩展? 性能更高? 更多“Pythonic”?【问题讨论】:
试试 codereview.SE 来做这类事情。我会投票关闭,但迁移选项非常有限(恕我直言,这是一个奇怪的选择)。 使用循环。对副作用的理解很烦人。 Python 不是 lisp。 您应该阅读有关如何验证您的json
数据并使用 JSON Schema 指定显式架构约束的信息。这个库在这里有它的 python 实现:jsonschema package
@MihaiAndrei 他们正在建立一个列表,因为他们需要一个列表,这不是副作用。可以说,唯一可以消除理解的是错误处理,它可以在迭代开始之前执行一次
【参考方案1】:
您应该阅读有关如何验证 json
数据并指定显式架构约束的信息
JSON Schema
该库允许您设置所需的键、指定默认值、添加类型验证等。
这个库在这里有它的python实现: jsonschema package
示例:
from jsonschema import Draft6Validator
schema =
"$schema": "https://json-schema.org/schema#",
"type": "object",
"properties":
"name": "type": "string",
"email": "type": "string",
,
"required": ["email"]
Draft6Validator.check_schema(schema)
【讨论】:
是的,这可能是“正确”的方法,而不是尝试将转换和验证组合到数据结构的单通道中。 (我倾向于尝试在不需要的地方进行微优化。)【参考方案2】:我只会使用一个普通的循环,但如果你将语句放入一个函数中,你可以将它添加到第一个条件检查中:
def type_check(type):
if type['type'] == 'test':
raise ValueError('sub-types of a [test] type can not contain key_value_pairs.')
return True
data_list = [
(
type['id'],
sub_type['id'],
key_value_pair['key'],
key_value_pair['value']
)
for type in my_parsed_json_array
if 'sub_types' in type
for sub_type in type['sub_types']
if 'key_value_pairs' in sub_type and type_check(type)
for key_value_pair in sub_type['key_value_pairs']
]
【讨论】:
这将调用json中每一行的测试类型。 @BearBrown 你的意思是外部列表中的每个字典?是的,我认为这就是重点。 是的,我是这样认为的,但是您的解决方案应该对 OP 有所帮助。【参考方案3】:你可以尝试类似的架构
def validate_top(obj):
if obj['type'] in BAD_TYPES:
raise ValueError("oof")
elif obj['type'] not in IRRELEVANT_TYPES: # actually need to include this
yield obj
def validate_middle(obj):
# similarly for the next nested level of data
# and so on
[
make_row(r)
for t in validate_top(my_json)
for m in validate_middle(t)
# etc...
for r in validate_last(whatever)
]
我这里的一般模式是使用生成器(函数,而不是表达式)来处理数据,然后用理解来收集它。
在更简单的情况下,不值得分离出多个处理级别(或者它们不自然存在),您仍然可以编写一个生成器并执行类似list(generator(source))
的操作。在我看来,这仍然比使用普通函数和手动构建列表更干净——它仍然将“处理”与“收集”问题区分开来。
【讨论】:
我喜欢,除了一个警告。validate_last()
需要与 t
一起提供,以便在我的示例中强制执行约束。 ('test'
类型很好,它是key_value_pairs
,底层,当t['type'] == 'test'
...时被限制为“非法”)以上是关于Python 在列表理解中引发错误(或更好的选择)的主要内容,如果未能解决你的问题,请参考以下文章
为啥我在 Python 中通过 reduce 对列表进行排序的代码会引发错误?