Yam Mesicka
Yam Mesicka

Reputation: 6581

Nested dictionary comprehension: Too many values to unpack

I try to rewrite the following code to work with dictionary comprehension, just for fun:

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)

I want to replace this line:

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

With a better looking dictionary comprehension, which might look like:

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

When I try to do so, I get the following error:

ValueError: too many values to unpack (expected 2)

What am I doing wrong? Using ipdb.pm() I saw that p is name=yam, which is good, but key and value are undefined.

Upvotes: 2

Views: 1382

Answers (1)

Martijn Pieters
Martijn Pieters

Reputation: 1122312

You can't do this:

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

because that requires every result of the p.split() call to have exactly two elements. Instead, you just have a sequence of single (string) elements, of variable length.

You'd have to wrap p.split() into another iterable first:

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

So now instead of:

['key', 'value']

You get:

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

which iterates just once, providing two values to unpack.

However, you could just use the dict() callable here; it consumes an iterable of (key, value) pairs directly:

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

You should also try to avoid reading the whole file into memory, you can use the file as an iterable directly:

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

Upvotes: 6

Related Questions