args and kwargs in Python, what Happens if You Don’t Use it?

If you've been coding in Python for a while, you've probably come across *args and **kwargs in function definitions. These special syntax features allow you to write more flexible and powerful functions by accepting a variable number of arguments. But what exactly are they, and how do they work? In this article, we’ll break down *args and **kwargs, explore how they work, and show why they're so useful in Python.

What Happens if You Don’t Use *args?

Let’s compare this with a function that doesn’t use *args. If you try to write a function to greet exactly three people, you might define it like this:

def greet(name1, name2, name3):
print(f"Hello, {name1}!")
print(f"Hello, {name2}!")
print(f"Hello, {name3}!")

This function works, but it’s highly limited. It can only accept exactly three arguments. If you try to pass fewer or more names, you’ll get an error:

greet("Alice", "Bob")  # Error: missing 1 required positional argument
greet("Alice", "Bob", "Charlie", "Dave") # Error: takes 3 positional arguments but 4 were given

As you can see, the function is rigid—it won’t work unless you give it the exact number of arguments it expects. This is where *args shines, allowing you to handle any number of arguments without changing the function definition.

But What Are Positional Arguments?

Before we dive further into *args, let’s clarify what positional arguments are. In Python, when you pass arguments to a function, their order matters—this is known as positional arguments. Here’s an example:

def greet(name, age):
print(f"Hello, {name}! You are {age} years old.")

In this case, name and age are positional arguments, which means the order in which you provide them when calling the function is crucial.

greet("Alice", 30)

This will output:

Hello, Alice! You are 30 years old.

But if you swap the order of the arguments:

greet(30, "Alice")

You’ll get an incorrect result:

Hello, 30! You are Alice years old.

So, with positional arguments, the order matters. This is important when we talk about *args because it allows you to handle multiple positional arguments dynamically.

Check this example:

What is *args?

*args allows a function to accept any number of non-keyword arguments. These arguments are packed into a tuple, which means you can access them by looping over the tuple or by their index. Let’s take a look at how this works in practice.

def greet(*args):
for name in args:
print(f"Hello, {name}!")

In the example above, the function greet can take any number of arguments, thanks to *args. When you call the function:

greet("Alice", "Bob", "Charlie")

The output will be:

Hello, Alice!
Hello, Bob!
Hello, Charlie!

Here, *args gathers all the arguments passed to the function into a tuple, allowing us to process them dynamically.

What is **kwargs?

Now that we understand positional arguments, let’s talk about **kwargs. While *args is used for non-keyword arguments, **kwargs handles keyword arguments, allowing you to pass named arguments to a function. These are stored in a dictionary, so you can access both the keys and values.

Here’s how you can use **kwargs in a function:

def greet_with_title(**kwargs):
for title, name in kwargs.items():
print(f"Hello, {title} {name}!")

Let’s call the function with keyword arguments:

greet_with_title(Mr="Smith", Mrs="Johnson", Dr="Adams")

The output will be:

Hello, Mr Smith!
Hello, Mrs Johnson!
Hello, Dr Adams!

As you can see, **kwargs allows you to handle any number of keyword arguments, making the function much more flexible.

What Happens if You Don’t Use **kwargs?

Without **kwargs, you would need to explicitly define every keyword argument in the function signature. For example:

def greet_with_title(Mr, Mrs, Dr):
print(f"Hello, Mr {Mr}!")
print(f"Hello, Mrs {Mrs}!")
print(f"Hello, Dr {Dr}!")

This works fine, but it’s limited. What if you need to add another title like Prof or Miss? You’d have to change the function definition:

greet_with_title(Mr="Smith", Mrs="Johnson", Dr="Adams", Prof="Brown")  # Error: got an unexpected keyword argument 'Prof'

By using **kwargs, you don’t have to modify the function to handle new arguments. It can accept any number of named arguments dynamically.

Using *args and **kwargs Together

Now you might be asking, “Can I use *args and **kwargs in the same function?” Absolutely! Let’s see how this works:

def greet_all(greeting, *args, **kwargs):
for name in args:
print(f"{greeting}, {name}!")

for title, name in kwargs.items():
print(f"{greeting}, {title} {name}!")

This function accepts a greeting (a required argument), a variable number of positional arguments using *args, and a variable number of keyword arguments using **kwargs.

Here’s how you can call this function:

greet_all("Good morning", "Alice", "Bob", Dr="Adams", Prof="Brown")

The output will be:

Good morning, Alice!
Good morning, Bob!
Good morning, Dr Adams!
Good morning, Prof Brown!

This function can now handle both positional and keyword arguments, making it highly flexible and adaptable to different inputs.

LEarn also the Difference between Python Methods and Functions

Conclusion

Using *args and **kwargs allows you to write more dynamic and reusable functions. Whether you need to handle a variable number of positional arguments (*args) or keyword arguments (**kwargs), these features give you the flexibility to adapt your functions to different situations without needing to rewrite them each time.

If you're building more complex applications or libraries, understanding *args and **kwargs is crucial for writing clean, maintainable, and scalable code. Give them a try in your next project, and you’ll see how powerful they can be!

Happy coding!

Published