Explain Codes LogoExplain Codes Logo

Use of *args and **kwargs

python
args
kwargs
decorators
Anton ShumikhinbyAnton Shumikhin·Aug 8, 2024
TLDR

When you need the versatility of handling multiple unnamed positional arguments in a Python function, *args steps in, morphing them into a tuple. It's a handy feature for cases where you can't predict the number of arguments the function might need.

def print_args(*args): print(args) # Prints all provided arguments

For those occasions when your functions could be dealing with varying named parameters, **kwargs is at your service. It packs keyword arguments into a dictionary.

def print_kwargs(**kwargs): print(kwargs) # Prints all provided keyword arguments

In your function's parameters, *args and **kwargs can handle anything you throw at them. Just remember to list *args before **kwargs.

def print_all(*args, **kwargs): print(args, kwargs) # Prints both arguments and keyword arguments

By enabling flexible argument passing, you can fashion engagingly adaptable functions without the need to define the number of parameters upfront.

Mastering *args and **kwargs

The proper pecking order

Striking the correct balance in ordering matters when *args and **kwargs are in play. The rigid sequence is: positional arguments, *args, **kwargs.

def example_func(arg1, arg2, *args, **kwargs): # arg1 and arg2 are mandatory. # *args catches additional positional arguments. # **kwargs catches all keyword arguments.

Trying to make a function call? Good to remember that explicit arguments get first dibs:

def example_func(arg, *args, **kwargs): pass example_func(1, 2, 3, arg=4) # arg is now 1, not 4! Explicit arguments are the seniors here.

Subclassing made easy

You want to subclass without touching the sacred parent's __init__ parameters? *args and **kwargs make it possible:

class BaseClass: def __init__(self, a, b): # Some initialization class DerivedClass(BaseClass): def __init__(self, c, *args, **kwargs): super().__init__(*args, **kwargs) # Heirarchy matters! # Additional initialization with "c"

Conditional programming

With *args and **kwargs, you can test for the presence of optional arguments and take actions accordingly:

def example_func(*args, **kwargs): if args: # Checking if we have company # Do something with the args if kwargs: # Any kwargs around? # Do something with the kwargs

Powering up your code with *args and **kwargs

String formatting on steroids

**locals() is an excellent tool for using **kwargs to smartly inject local variables into strings:

def format_string(name, age): return "Name: {name}, Age: {age}".format(**locals()) # Collecting data from locals. Smart, isn't it?

Decorators reimagined

Decorators love *args and **kwargs - they can transparently handle any number of parameters:

from functools import wraps def my_decorator(func): @wraps(func) def wrapper(*args, **kwargs): # Possible world domination actions before the original function call result = func(*args, **kwargs) # Time to celebrate after the function call return result return wrapper # Not just a wrapper, but a totally souped-up wrapper

Anticipating change

*args and **kwargs can aid in crafting forward-compatible functions that age gracefully:

# New arguments can be added in the future with no one breaking a sweat def api_function(a, *args, **kwargs): # Processing "a". "a" is feeling special # *args and **kwargs will handle all the future extras

Debugging assistants

Capturing all incoming arguments can provide a godsend during debugging and logging scenarios:

def debug_function(*args, **kwargs): print("Args:", args) # Show me what you got! print("Keyword Args:", kwargs) # Let's see the specials! # Carry on with the function logic here