Диктант Python внутри списка внутри диктанта — понимание возможно?

#python #python-3.x

Вопрос:

У меня есть объект в Python 3 такого формата:

 a = {
        'events': [
            {
                'timestamp': 123,
                'message': 'test'
            },
            {
                'timestamp': 456,
                'message': 'foo'
            },
            {
                'timestamp': 789,
                'message': 'testbar'
            },
        ],
        'first': 'abc',
        'last': 'def'
    }
 

Я хочу создать новый объект того же формата, но отфильтрованный по тому, содержит ли соответствующее message значение ключа определенную строку, например, отфильтровав по "test" :

 a = {
        'events': [
            {
                'timestamp': 123,
                'message': 'test'
            },
            {
                'timestamp': 789,
                'message': 'testbar'
            },
        ],
        'first': 'abc',
        'last': 'def'
    }
 

Могу ли я использовать для этого вложенное понимание? Я знаю, что вы можете выполнять понимание вложенных списков, например:

 [[y*2 for y in x] for x in l]
 

Но есть ли аккуратный выход dict > list > dict из ситуации?

Комментарии:

1. Вы можете использовать понимание списка для событий . Более общее решение dict будет зависеть от удобочитаемости/ремонтопригодности.

Ответ №1:

Одним из вариантов может быть создание новой копии входного диктанта без событий, а затем установка отфильтрованных событий по вашему требованию, например:

 copy = {k: v for k, v in a.items() if k != 'events'}
copy['events'] = [e for e in a['events'] if 'test' in e['message']]
 

Или, если вы не возражаете против перезаписи исходных данных, просто сделайте это:

 a['events'] = [e for e in a['events'] if 'test' in e['message']]
 

Ответ №2:

Я бы пошел с пониманием списка с помощью if-утверждения, подобного следующему:

 [event for event in a["events"] if event["message"] == "test" ]
 

Просмотрите значения "events" -ключа и добавьте их в список, если значение их "message" ключа равно "test" .

В результате вы получите список словарей, которые вы можете назначить обратно a["events"] , или копию a , если хотите сохранить a["events"] .

Ответ №3:

Итак, вы можете использовать несколько уровней понимания, но это не значит, что вы должны это делать. Я думаю, что для такого примера вы бы создали более чистый код, запустив его через пару циклов for. Сказав это, я думаю, что следующее технически достигает результата, о котором вы просите.

 >>> pprint.pprint(a) 
{'events': [{'message': 'test', 'timestamp': 123},
            {'message': 'foo', 'timestamp': 456},
            {'message': 'testbar', 'timestamp': 789}],
 'first': 'abc',
 'last': 'def'}
>>> aa = copy.deepcopy(a)
>>> aa['beta'] = aa['events']
>>> pprint.pprint({k:[item for item in v if 'test' in item['message']] if isinstance(v, list) else v for k, v in aa.items()})
{'beta': [{'message': 'test', 'timestamp': 123},
          {'message': 'testbar', 'timestamp': 789}],
 'events': [{'message': 'test', 'timestamp': 123},
            {'message': 'testbar', 'timestamp': 789}],
 'first': 'abc',
 'last': 'def'}
>>> pprint.pprint({k:[item for item in v if 'test' in item['message']] if isinstance(v, list) else v for k, v in a.items()})
{'events': [{'message': 'test', 'timestamp': 123},
            {'message': 'testbar', 'timestamp': 789}],
 'first': 'abc',
 'last': 'def'}

 

Как уже было сказано, это то, что вы можете сделать; однако я бы от имени всех, кому приходилось читать код других людей в своей карьере, с уважением попросил вас не использовать это в производственном коде. Пара циклов for может быть более локальными, но в большинстве случаев они будут гораздо более удобочитаемыми и обслуживаемыми.