Python: Unpacking dictionary with integer keys?

Viewed 873

For a dictionary as follows:

my_dict={'name':'Stack', 'age':11}
print('Name is {name} and age is {age}.'.format(**my_dict))
#output# Name is Stack and age is 11

However, if keys are integers in a dictionary, how should I unpack the dictionary, for example:

new_dict={1:'Stack', 2:'over', 3:'flow'}
print('1 is {1} and 2 is {2} and 3 is {3}'.format(**new_dict))
#output error# tuple index out of range

In this case, how can I get result as follows:

1 is Stack and 2 is over and 3 is flow

I know one can do it with many other ways, but is it possible to use the same strategy in the first example. Thank you.

3 Answers

You can't do that unpacking. What you need is something else.

** in a function call unpacks a dict into keyword arguments, and keyword argument names are always strings. An int 1 isn't a valid keyword argument name. Even if it was, or if you converted the dict keys to strings, str.format wouldn't look for such a keyword argument.

{1}, {2}, etc. in format look for positional arguments, not keyword arguments. No ** unpacking will produce positional arguments. Also, since they're positional, if you start at 1, you would need to have a dummy argument in position 0 so the rest of the arguments can go in the right positions.

If you really want to do this with a dict and a format string that looks like that, probably the easiest route would be to go through a string.Formatter subclass and override get_value:

import string

class IntDictFormatter(string.Formatter):
    def get_value(self, key, args, kwargs):
        return kwargs[key]

format_string = '1 is {1} and 2 is {2} and 3 is {3}'

value_dict = {1:'Stack', 2:'over', 3:'flow'}

print(IntDictFormatter().vformat(format_string, (), value_dict))

You can use dict.values(), and only use one *:

new_dict={1:'Stack', 2:'over', 3:'flow'}

print('1 is {} and 2 is {} and 3 is {}'.format(*new_dict.values()))

Output:

1 is Stack and 2 is over and 3 is flow

The reason you get an error is because, you know that in f-strings, the things in the curly braces are evaluated during runtime.

And so, having numbers as keys will make python think the keys are regular integers.

UPDATE:

new_dict={1:'Stack', 2:'over', 3:'flow'}

print(f'3 is {new_dict[3]} and 1 is {new_dict[1]} and 2 is {new_dict[2]}')

Output:

3 is flow and 1 is Stack and 2 is over
my_dict={'name':'Stack', 'age':11, 1:'Stack', 2:'over', 3:'flow'}

for k, v in my_dict.items():
    if k == list(my_dict.keys())[-1]:
        print(k, 'is', v)
    else:
        print(k, 'is', v, 'and ', end='')

it outputs:

name is Stack and age is 11 and 1 is Stack and 2 is over and 3 is flow
Related