Ошибки Python Raising в пределах понимания списка (или лучшая альтернатива)
У меня есть вложенная структура, прочитанная из строки 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, который чувствует себя избыточным. (Каждый набор пар ключ-значение может иметь тысячи пар, и проверку нужно выполнить только один раз, чтобы убедиться, что все в порядке.)
Также будут другие правила, аналогичные этим, которые применяются на разных уровнях иерархии. Такие как "тестовые" типы могут содержать только "sub_test" sub_types.
Какие варианты, кроме указанных выше?
- Более элегантно?
- Более расширяемый?
- Более производительный?
- Больше "Pythonic"?
3 ответа
Вы должны прочитать о том, как проверить свои json
данные и задание явных ограничений схемы с помощью схемы JSON. Эта библиотека позволяет вам устанавливать необходимые ключи, указывать значения по умолчанию, добавлять проверку типа и т. д.
Эта библиотека имеет реализацию на python: пакет jsonschema
ПРИМЕР:
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)
Я бы просто использовал простой цикл, но вы можете добавить его к первой условной проверке, если вы поместите оператор в функцию:
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']
]
Вы можете попробовать архитектуру в соответствии с
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))
, На мой взгляд, это все же чище, чем использование обычной функции и составление списка вручную - он по-прежнему отделяет задачи "обработка" от "сбора".