Skip to content

RESOURCES / BLOG

How to pack and unpack arguments in Python functions?

If you read Python threads in dev communities, you often see snippets that use asterisks in surprising ways: passing lists into functions with a star, forwarding *args and **kwargs, or merging dictionaries on the fly. These patterns make your APIs flexible and your call sites clean, but they can be confusing until you see them side by side with examples.

Hi everyone,
I keep seeing *args and **kwargs in function signatures and calls, along with sequence and dict unpacking in assignments and merges. Could someone explain clearly how to pack and unpack arguments in Python functions? I want to understand the syntax, common pitfalls, and best practices for writing flexible functions and forwarding parameters.

Python allows you to pass functions with a variable number of arguments instead of only what’s hard-coded. This can be done with the unpacking operator *, letting you store arguments in a tuple, and looping over the arguments as an iterator. Let’s break it down.

  • Packing: Collect multiple positional arguments into a tuple via *args and multiple keyword arguments into a dict via **kwargs.
  • Unpacking: Expand a sequence into positional arguments with * or a mapping into keyword arguments with ** at the call site or assignment.
def greet(greeting, *names, punctuation="!"):
    # names is a tuple of any extra positional args
    joined = ", ".join(names) if names else "there"
    return f"{greeting}, {joined}{punctuation}"

def configure(a, b=0, *, verbose=False, **options):
    # verbose is keyword-only, options captures extra keywords
    return a, b, verbose, options

def coords(x, y, /, *, unit="px"):
    # x and y are positional-only, unit is keyword-only
    return f"{x},{y} {unit}"Code language: PHP (php)
vals = [1, 2, 3]
print(sum(*[vals]))  # same as sum(vals)

pair = (5, 7)
print(coords(*pair, unit="cm"))  # expands to coords(5, 7, unit="cm")

payload = {"greeting": "Hello", "punctuation": "!!"}
print(greet(**payload, *["Alice", "Bob"]))  # mix unpacking in callsCode language: PHP (php)
def logging_wrapper(func, *args, **kwargs):
    print("Calling", func.__name__, "with", args, kwargs)
    return func(*args, **kwargs)

def base(*args, retries=3, **kwargs):
    # Forward with defaults
    return do_work(*args, retries=retries, **kwargs)Code language: PHP (php)
# Starred assignment
first, *middle, last = [10, 20, 30, 40]
# first=10, middle=[20, 30], last=40

# Dict merges and overrides
defaults = {"quality": "auto", "format": "jpg"}
overrides = {"format": "webp", "width": 800}
merged = {**defaults, **overrides}
# merged == {"quality": "auto", "format": "webp", "width": 800}Code language: PHP (php)
  • Order matters in signatures. positional, *args, keyword-only, **kwargs.
  • Order matters in dict merges. later keys override earlier ones.
  • Do not overuse **kwargs if a clear, explicit API is better. Use it when forwarding or supporting extensibility.
  • When forwarding, document which **kwargs you honor and pass the rest through.

After you understand the generic patterns, you can apply them to SDKs. With Cloudinary’s Python SDK, it is common to compose upload options and transformation steps dynamically, then unpack them into a single call.

import cloudinary.uploader as uploader

DEFAULT_UPLOAD_OPTS = {
    "folder": "samples",
    "overwrite": True,
    "tags": ["demo"]
}

user_overrides = {
    "public_id": "avatar_123",
    "resource_type": "image"
}

# Merge defaults with user overrides
upload_opts = {**DEFAULT_UPLOAD_OPTS, **user_overrides}

# Build a transformation pipeline dynamically
base = {"width": 800, "height": 800, "crop": "fill"}
watermark = {"overlay": "logo", "gravity": "south_east", "x": 15, "y": 15, "opacity": 70}
extra = {"fetch_format": "auto", "quality": "auto"}

# transformation can be a list of dicts
transformations = [base, watermark, extra]

# Unpack the dict as keyword args, pass the list directly
result = uploader.upload(
    "local/path/to/image.jpg",
    transformation=transformations,
    **upload_opts
)
print(result["secure_url"])Code language: PHP (php)

Packing lets you keep clean defaults while letting callers override exactly what they need. Unpacking ensures concise, readable calls. 

  • *args packs extra positional args; **kwargs packs extra keyword args.
  • Unpack with * and ** at call sites, in assignments, and for dict merges.
  • Prefer explicit parameters for core API, but use forwarding with *args and **kwargs to keep wrappers flexible.
  • When merging dicts, later values win. Document which kwargs you consume vs forward.
  • These patterns map neatly onto real SDKs like Cloudinary for building dynamic, configurable calls.

Ready to streamline your media workflow and try these patterns in production? Sign up for a free Cloudinary account and start building faster, more flexible pipelines.

Start Using Cloudinary

Sign up for our free plan and start creating stunning visual experiences in minutes.

Sign Up for Free