Понимание вложенного словаря: слишком много значений для распаковки

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

import itertools

with open('foo.txt') as f:
    entities = f.read().splitlines()

parsed_entities = []
while entities:
    props = itertools.takewhile(lambda n: n != 'EOM', entities)
    entity = {p.split('=')[0]: p.split('=')[1] for p in props}
    entities = entities[len(entity)+2:]  # Delete and skip EOM & newline
    parsed_entities.append(entity)

Я хочу заменить эту строку:

entity = {p.split('=')[0]: p.split('=')[1] for p in props}

С улучшенным пониманием словаря, которое может выглядеть так:

entity = {key: value for p in props for key, value in p.split('=')}

Когда я пытаюсь это сделать, я получаю следующую ошибку:

ValueError: слишком много значений для распаковки (ожидается 2)

Что я делаю не так? Используя ipdb.pm(), я увидел, что p равно name=yam, что хорошо, но key и value не определены.


person Infinity    schedule 01.07.2017    source источник
comment
Я думаю, что длина p.split('=') не равна 2.   -  person Sam Chats    schedule 01.07.2017
comment
@SamChats: конечно. А вот p.split('=')[0] нет, вот и распаковывается.   -  person Martijn Pieters    schedule 01.07.2017
comment
@SamChats, проверил. Я заменил p.split('=') на print(len(p.split('='))), получилось 2.   -  person Infinity    schedule 01.07.2017
comment
@MartijnPieters Понял вашу точку зрения. Довольно познавательно :)   -  person Sam Chats    schedule 01.07.2017


Ответы (1)


Вы не можете сделать это:

for key, value in p.split('=')

потому что для этого требуется, чтобы каждый результат вызова p.split() содержал ровно два элемента. Вместо этого у вас просто есть последовательность одиночных (строковых) элементов переменной длины.

Сначала вам нужно обернуть p.split() в другую итерацию:

entity = {key: value for p in props for key, value in (p.split('='),)}

Итак, теперь вместо:

['key', 'value']

Вы получаете:

(['key', 'value'],)

который повторяется один раз, предоставляя два значения для распаковки.

Однако здесь вы можете просто использовать вызываемый объект dict(); он использует итерацию из (key, value) пар напрямую:

entity = dict(p.split('=') for p in props)

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

from itertools import takewhile

parsed_entities = []
with open('foo.txt') as f:
    cleaned = (l.rstrip('\n') for l in f)
    while True:
        props = takewhile(lambda n: n != 'EOM', cleaned)
        parsed_entities.append(dict(p.split('=') for p in props))
        try:
            next(cleaned)  # consume line after EOM
        except StopIteration:
            # no more lines
            break
person Martijn Pieters    schedule 01.07.2017
comment
Ваше решение работает как по волшебству, спасибо! Но я хочу попробовать и понять. Печать p.split('=') приводит к ['a', 'b']. Почему вы говорите, что это последовательность переменной длины? - person Infinity; 01.07.2017
comment
@Infinity: 'a' - это строка длины один, но вы можете иметь 'foo' или 'bar' или более длинные или короткие строки. Цикл for выполняет итерацию по этим двум строкам, а не по их паре, поэтому для ['a', 'b'] вы сначала пытаетесь выполнить key, value = 'a'. Это не сработает, потому что строка длины один содержит только один символ. Но подойдет key, value = 'ac', это строка длины 2. - person Martijn Pieters; 01.07.2017
comment
Наконец-то понял. Спасибо :) - person Infinity; 01.07.2017