function definition
optional arguments
programming
coding
Python

How do I define a function with optional arguments?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Optional arguments let one function handle both the common case and a more configurable case without forcing every caller to supply every parameter. In Python, the standard mechanism is default parameter values, but there are a few related patterns that matter if you want the function to stay readable and bug-free.

Use Default Values in the Signature

An argument becomes optional when you give it a default value.

python
1def greet(name, punctuation="!"):
2    return f"Hello, {name}{punctuation}"
3
4
5print(greet("Ada"))
6print(greet("Ada", "?"))

This is the core rule. If the caller omits punctuation, Python uses "!".

Required parameters must come first. Optional parameters with defaults come after them:

python
def connect(host, port=5432, use_ssl=False):
    return host, port, use_ssl

This ordering is invalid:

python
# SyntaxError
def broken(port=5432, host):
    return host, port

Prefer Named Calls for Optional Settings

Once a function has more than one optional flag, keyword arguments make the call site much easier to understand.

python
1def create_user(username, is_admin=False, send_email=True, locale="en"):
2    return {
3        "username": username,
4        "is_admin": is_admin,
5        "send_email": send_email,
6        "locale": locale,
7    }
8
9
10print(create_user("mark"))
11print(create_user("mark", send_email=False, locale="fr"))

Compare that second call with a positional version such as create_user("mark", False, False, "fr"). The keyword version is far easier to read.

Use Keyword-Only Optional Arguments When Clarity Matters

If you want to prevent callers from passing certain optional arguments positionally, add * in the function signature.

python
1def save_report(name, *, overwrite=False, compress=True):
2    return name, overwrite, compress
3
4
5print(save_report("daily.csv", overwrite=True))

Now overwrite and compress must be passed by name. This is a good pattern for configuration flags that would otherwise be ambiguous at the call site.

Avoid Mutable Default Values

The most important trap with optional arguments in Python is that default values are evaluated once, when the function is defined. So mutable defaults are reused across later calls.

Bad example:

python
def add_tag(tag, tags=[]):
    tags.append(tag)
    return tags

That reuses the same list over time. The safe pattern is to use None as a sentinel and allocate inside the function.

python
1def add_tag(tag, tags=None):
2    if tags is None:
3        tags = []
4    tags.append(tag)
5    return tags
6
7
8print(add_tag("python"))
9print(add_tag("kubernetes"))

This pattern also applies to dictionaries, sets, timestamps, and any other value that should be freshly created for each call.

Optional Arguments Versus *args and **kwargs

Sometimes developers mix up optional arguments with *args and **kwargs. They solve different problems.

  • default values define known optional parameters
  • '*args collects extra positional arguments'
  • '**kwargs collects extra named arguments'

Example:

python
1def log_event(event_name, *messages, level="INFO", **context):
2    return {
3        "event": event_name,
4        "messages": messages,
5        "level": level,
6        "context": context,
7    }
8
9
10event = log_event(
11    "user_login",
12    "validated credentials",
13    "created session",
14    level="DEBUG",
15    user_id=42,
16    source="web",
17)
18
19print(event)

This is useful when the function truly accepts variable extra data. If the set of options is known ahead of time, normal named parameters are usually clearer.

Optional Arguments Can Improve API Design

Good optional parameters make a function easy to use in the common case and flexible in the uncommon case. Bad optional parameters create a giant function with too many loosely related flags.

A reasonable function:

python
def fetch(url, timeout=10, retries=2):
    return url, timeout, retries

A less healthy design is a function with many unrelated booleans and magic defaults that callers struggle to remember. In those cases, two smaller functions or a configuration object may be cleaner.

Common Pitfalls

The biggest pitfall is using mutable defaults such as [] or dict(). Another is overusing positional calls for optional settings, which makes function calls hard to read and easy to break. Developers also sometimes use **kwargs for everything when explicit named parameters would have been clearer. Finally, remember that defaults are evaluated once at definition time, so dynamic values such as the current time should be created inside the function body, not in the signature.

Summary

  • Define optional arguments by assigning default values in the function signature.
  • Put required parameters before optional ones.
  • Prefer keyword arguments for readability when several optional settings exist.
  • Use keyword-only parameters with * when callers should name specific options.
  • Use None instead of mutable objects for safe dynamic defaults.
  • Use *args and **kwargs only when the function truly needs flexible extra input.

Course illustration
Course illustration

All Rights Reserved.