Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

Python Python Collections (2016, retired 2019) Dictionaries String Formatting with Dictionaries

Daniel McFarlin
Daniel McFarlin
5,168 Points

unpacking coding challenge question

Hey everyone, I've been struggling with this concept for a while now. Packing and unpacking is still not quite making sense my brain yet. Can someone help me with this question and sorta break down the code in another way so I can try to understand this concept more thoroughly? That would be much appreciated! Thank You! :)

string_factory.py
# Example:
# values = [{"name": "Michelangelo", "food": "PIZZA"}, {"name": "Garfield", "food": "lasagna"}]
# string_factory(values)
# ["Hi, I'm Michelangelo and I love to eat PIZZA!", "Hi, I'm Garfield and I love to eat lasagna!"]

template = "Hi, I'm {name} and I love to eat {food}!"
values = [{ 'name':'mike', 'food':'pizza'},  {'name': 'garfield', 'food': 'lasagna'}]


def string_factory(list_of_dicts):
   list_of_str = []
   for dic in list_of_dicts:
       list_of_str.append(unpacker(**dic))
   return list_of_str


def unpacker(name=None, food=None):
   template = "Hi, I'm {} and I love to eat {}"
   return template.format(name, food)



print(string_factory(values))

2 Answers

Chris Freeman
MOD
Chris Freeman
Treehouse Moderator 68,441 Points

** is the keyword argument unpacking symbol. It is used to expand a dictionary into keyword arguments.

Let's start with making a dict. There are many syntactical ways to make a dict. The Python 3 std types doc shows many ways. Let's focus on the first two:

>>> a = dict(one=1, two=2, three=3)  # using keyword arguments
>>> b = {'one': 1, 'two': 2, 'three': 3}  # using key value pairs (common way)
>>> a == b
True

The ** will unpack (or "expand") a dict from the key/value pair format to the keyword argument format.

>>> dct = dict(a=3,b=4,c=5)
>>> dct
{'a': 3, 'c': 5, 'b': 4}
# print the dct contents explicitly
>>> print("{a} {b} {c}".format(a=dct['a'], b=dct['b'], c=dct['c']))
3 4 5
# The ** will do this unpacking for us. 
# The keywords are taken from the dict keys, the values are the dict values
>>> print("{a} {b} {c}".format(**dct))
3 4 5

One huge advantage with unpacking is the order of the keys do no matter since the format print fields have the same name of the dict keys. That is, the format matches arguments to fields by name when using keyword arguments.

>>> print("{a} {b} {c}".format(a=dct['a'], c=dct['c'], b=dct['b']))  # argument order does not matter
3 4 5

When used as a formal parameter (in the definition of a function or method), ** will receive keyword arguments and pack them into the dict specific by the ** prefix.

# pack all keyword arguments in to "kwargs" dict. "kwargs" is just convention. Could have been "**bob"
>>> def print_args(**kwargs):
...     for keyword, value in kwargs.item():  # use kwargs like a dict 
...         print("kw: {}, value: {}".format(keyword, value))
>>> print_args(a="first", b="next", c="last")
kw: a, value: first
kw: c, value: last
kw: b, value: next

Your posted is very close and will pass with two changes.

template = "Hi, I'm {name} and I love to eat {food}!"

def string_factory(list_of_dicts):
    list_of_str = []
    for dic in list_of_dicts:
        list_of_str.append(unpacker(**dic))
    return list_of_str


def unpacker(name=None, food=None):
    template = "Hi, I'm {name} and I love to eat {food}!" # missing ! at end of line
    return template.format(name=name, food=food)  # need to use keyword arguments

But this is working too hard. Instead of adding an unpacker function, the ** can be used directly:

template = "Hi, I'm {name} and I love to eat {food}!"

def string_factory(list_of_dicts):
    list_of_str = []
    for dic in list_of_dicts:
        list_of_str.append(template.format(**dic))
    return list_of_str

Note: you don't have to provide the values or call the string_factory function. The challenge checker will cover running the code and providing the necessary arguments.

Please post back if you need more help. Good luck!!!