HOLIDAY SALE! Save 50% on Membership with code HOLIDAY50. Save 15% on Mentorship with code HOLIDAY15.

4) Functions and Scope Lesson

Python Args and Kwargs

12 min to complete · By Martin Breuss

The last time you were floating around StackOverflow researching Python topics in your free time, you might have come across the words *args and **kwargs. They arguably sound like guttural sounds that a frog might make rather than the elegant zssssh of a python. So, it's no wonder that these special arguments can cause a bit of confusion:

Frog on a stone - Photo by https://unsplash.com/@abuchotlz Austin Santaniello Bucholtz on Unsplash

Photo by Austin Santaniello Bucholtz https://unsplash.com/@abuchotlz

But when it comes down to it, frogs are just animals, and *args and **kwargs are just another useful feature of the Python language.

The two words *args and **kwargs are conventional abbreviations:

  • args stands for arguments
  • kwargs stands for keyword arguments

Both names are replaceable variable names, but for everyone's sake, just stick with these names to help others and yourself remember the language feature you're using.

The syntax that actually provides the functionality of this feature is the asterisks (* and **).

When you define a function, you can prefix a parameter name, e.g., args and kwargs, with asterisks. By doing this, you implement a language feature that allows your function to now take arbitrary amounts of arguments.

But exactly does that mean, and what's the difference between *args and **kwargs?

Python Args: The Single Asterisk (*)

If you add *args as a parameter to your function definition, it will package all passed arguments into a collection. You can then access each item like you would in a normal list. More commonly, however, you'll just iterate over all items in args:

def print_args(*args):
    for a in args:
        print(a)

print_args("barcelona", "tahoe", "ubud", "koh tao")

# OUTPUT:
# barcelona
# tahoe
# ubud
# koh tao

In this code snippet, you're using *args in the first line of your function definition, just like you would otherwise define a single parameter that your function takes as its input.

However, by prepending the parameter name with the single asterisk (*), you invoke the special language feature that allows you to pass as many arguments to your function call as you want to.

Practice with print_args()

  • Copy the code into your local text editor.
  • Execute the script passing different amounts of arguments to print_args().

You might have noticed that the asterisk is only used inside the parentheses when you define your function. When you use the packaged arguments inside your function body, you only use the variable name args.

Python Kwargs: The Double-Asterisk (**)

Adding **kwargs to your function definition has a similar effect. However, Python packages up your arguments in a mapping instead.

This means that you need to pass the arguments as keyword arguments and access them in your function body as you would with a dictionary:

def print_kwargs(**kwargs):
    for k, v in kwargs.items():
        print(k, v)

print_kwargs(country='ukraine', city='odessa')

# OUTPUT:
# country ukraine
# city odessa

Here, you added **kwargs in the first line of your function definition.

By prepending the parameter name with the double-asterisk (**), you invoke the special language feature that allows you to pass as many keyword arguments to your function call as you want to.

Use Cases

To think of a practical use case for this feature and tie it back to your greet() function, imagine that you're handling a lot of users in your web app. You want to send them all a short email every month in order to check in and see how they like your product.

For this, you'll write a new function, greet_many(), based on greet(), with a few changes to incorporate the use of *args and allow the function to greet an arbitrary amount of users:

def greet_many(greeting, *args):
    greetings = ""
    for name in args:
        sentence = f"{greeting}, {name}! How are you?"
        greetings += sentence + "\n"
    return greetings

Your new greet_many() function takes a required positional argument, greeting, as well as an arbitrary amount of additional arguments packaged into the variable args.

Colorful illustration of a light bulb

Info: Note that if you are mixing defined positional parameters, such as greeting, with *args, then *args needs to come after any defined parameters in your function definition.

Because your function will only be able to return one value, you're creating a new string named greetings that you later concatenate each individual greeting sentence to, followed by a newline character ("\n") for better formatting.

When you collect arguments with *args, they get put into a collection, which is why you're introducing a for loop to access each item in the collection.

With greet_many(), you are now able to greet all your users with the same greeting message, and it doesn't matter how many users currently are in your app. *args will handle them, whether it's only one or 1000 of them.

In this example, you can also instead explicitly use a list or a tuple to pass all the names that you could have called names in a single argument. This approach would be more descriptive in this specific situation and goes to show that there are always multiple ways to achieve your aim when you're programming.

Colorful illustration of a light bulb

Info: You'll revisit the use of *args and **kwargs in another example later in the course when you'll learn about decorators.

Run the code in your local IDE and make sure that you can get your adapted greet_many() function to greet different amounts of users before moving on.

Practice with Kwargs

  • Refactor greet_many() to use **kwargs instead. What could you pass?
  • Do an online search and read through some StackOverflow posts about the topic.
  • How can you use *args and **kwargs when you're calling a function?
# your turn!

Maybe you noticed that it's helpful that you named your function descriptively as greet(). This helps to know what the function is about when you need to call it.

However, you currently don't have any information about how many arguments you need to provide to greet() or what it really does.

Using arbitrary-length arguments such as *args and **kwargs in your function definition can prevent errors when you want your function to be open for arbitrary-length inputs. You've looked at how to do this when writing greet_many().

Most of the time, however, you'll want your functions to be called in a very specific way, with a specific amount of inputs. greet() is meant to be called with exactly one greeting and exactly one name, both of which should be of the str data type. How can you communicate this to other developers?

In a future lesson, you'll learn how to add docstrings to your function definition. This adds standard documentation to it and makes it easier for you and other developers to work with the functions you're writing.

Colorful illustration of a light bulb

Additional Resources

Summary: Python Args and Kwargs

  • In this lesson, you learned about using *args and **kwargs to allow your functions to take arbitrary amounts of arguments:
    • *args stands for arguments and gathers all additional positional arguments into a collection.
    • **kwargs stands for keyword arguments and gathers all additional keyword arguments into a mapping.
  • You can also use the * and ** syntax to unpack collections and mappings when calling a function:
def greet(greeting, name):
    sentence = f"{greeting}, {name}! How are you?"
    return sentence

user_tuple = ("Hello", "Waheed")
print(greet(*user_tuple))  
# OUTPUT: Hello, Waheed! How are you?