Python use kwargs in a formatted string: expect string, get dict?

Viewed 299

I'm trying to figure out kwargs and do some simple string formatting.

def basic_human(first_name='Jeff', age=42):
    return f"My name is {first_name}, and my age is {age}"


def starwars_fan(movie='A new hope', jedi='Young Obi Wan', **kwargs):
    human_string = basic_human(kwargs)
    return f"{human_string}. My favorite movie is {movie}, and my favorite Jedi is {jedi}."

print(basic_human(first_name='Mr. Baby', age=0.8))
print(starwars_fan(person_name='Chris', jedi='Kit Fisto'))

In the first case, things work fine:

My name is Mr. Baby, and my age is 0.8

In the second case, the person_name parameter comes in as a dict and I'm not sure why:

My name is {'first_name': 'Chris'}, and my age is 42. My favorite movie is A new hope, and my favorite Jedi is Kit Fisto.

Is there a way to make this work without explicitly overriding each of the "basic human" params?

def basic_human(first_name='Jeff', age=42, other_params=None):
    if other_params:
        if 'first_name' in other_params:
            first_name = other_params['first_name']...
    return f"My name is {first_name}, and my age is {age}"
3 Answers

Change it to this:

human_string = basic_human(**kwargs)

kwargs is a dict containing all of the leftover parameters:

print(kwargs)
# {'first_name': 'Chris'}

The way you wrote it, basic_human's first argument (first_name) is this dict, its second (age) is 42, and other_params is still None.

In other words, your version is equivalent to:

# What you got, not what you want.
basic_human(first_name={'first_name': 'Chris'}, age=42, other_params=None)

What the ** in my version changes is that it expands the dict into arguments. Each key in kwargs becomes a named argument to basic_human, with the corresponding value. (See here for details.)

In order to pass kwargs through the the basic_human function, you need it to also accept **kwargs so any extra parameters are accepted by the call to it.

Secondly, you must pass through kwargs in the same way, i.e. by unpacking them to named arguments when passing them over to basic_human

Like so:

def basic_human(first_name='Jeff', age=42, **kwargs):
    return f"My name is {first_name}, and my age is {age}"


def starwars_fan(movie='A new hope', jedi='Young Obi Wan', **kwargs):
    human_string = basic_human(**kwargs)
    return f"{human_string}. My favorite movie is {movie}, and my favorite Jedi is {jedi}."

print(basic_human(first_name='Mr. Baby', age=0.8))
print(starwars_fan(person_name='Chris', jedi='Kit Fisto'))

You have to unpack the kwargs. You have to change it to human_string = basic_human(**kwargs). The ** unpacks the dict. You can also have a dict with the keys first_name and jedi keys and use ** to unpack it.

Also, you pass person_name='Chris' which doesn't exist. Did you mean first_name='Chris'?

Related