Decisions, Loops & FunctionsExtra· 40 min read

Functions in Depth

Default values, keyword arguments, *args and **kwargs, and how scope decides where a variable lives.

What you will learn

  • Give parameters default values
  • Accept any number of arguments with *args and **kwargs
  • Understand local vs global scope

Default values

A default value lets a parameter be optional — if the caller does not pass one, the function uses a fallback you chose. You set it with = right in the function definition.

A parameter with a default value
def greet(name, greeting="Hello"):
    return greeting + ", " + name + "!"

print(greet("Asha"))                 # uses the default
print(greet("Ravi", "Welcome"))      # overrides it

The parameter greeting="Hello" has a default. In the first call we pass only name, so greeting falls back to "Hello", giving Hello, Asha!. In the second call we pass a second value, "Welcome", which replaces the default, giving Welcome, Ravi!. Defaults make a function flexible without forcing the caller to supply every value.

Note: Output: Hello, Asha! Welcome, Ravi!

Keyword arguments

Normally Python matches arguments by position (first value to first parameter). But you can also pass them by name, called keyword arguments — then the order does not matter and the call reads more clearly.

Passing arguments by name
def book(room, nights, breakfast):
    return f"{room} for {nights} nights, breakfast={breakfast}"

print(book(nights=2, room="Deluxe", breakfast=True))

Even though we wrote nights first and room second, naming each argument tells Python exactly where it goes — so room still gets "Deluxe" and nights gets 2. Keyword arguments are great when a function has many parameters and you want the call to be self-explaining.

Note: Output: Deluxe for 2 nights, breakfast=True

*args: accept any number of values

Sometimes you do not know how many values the caller will pass — think of a function that adds up any amount of numbers. Putting a * before a parameter name (the convention is *args) collects all the extra positional values into a tuple for you.

*args gathers extra arguments into a tuple
def total(*numbers):
    result = 0
    for n in numbers:
        result = result + n
    return result

print(total(2, 3))          # 5
print(total(1, 2, 3, 4))    # 10

The *numbers parameter scoops up all the values passed in — in the first call that is (2, 3), in the second (1, 2, 3, 4). Inside the function numbers is just a tuple, so we loop over it and add everything up. The same function happily handles two numbers or four, because *args does not fix the count.

Note: Output: 5 10

**kwargs: accept named extras

The double-star version, **kwargs, collects any extra keyword arguments into a dictionary — each name becomes a key and each value a value. Handy when a function should accept flexible, named options.

**kwargs gathers named arguments into a dict
def profile(**details):
    for key, value in details.items():
        print(key, "->", value)

profile(name="Asha", age=22, city="Lucknow")

Each named argument in the call becomes a key–value pair inside the details dictionary: nameAsha, age22, cityLucknow. We then loop over .items() (just like a normal dict) and print each pair. The function never had to know in advance which details would be passed.

Note: Output: name -> Asha age -> 22 city -> Lucknow

Scope: where a variable lives

Scope is the rule for where a variable can be seen. A variable created inside a function is local — it exists only there and vanishes when the function ends. A variable created outside all functions is global — visible everywhere. This keeps each function tidy and self-contained.

Local variables do not leak out
message = "global"      # global variable

def show():
    message = "local"   # a separate local variable
    print("inside:", message)

show()
print("outside:", message)

There are two different message variables here. The one inside show() is local — it only exists during the call, so inside: local prints. The outer message is global and untouched, so after the function finishes, outside: global prints. Changing a name inside a function does not change the global one with the same name — they live in separate scopes.

Note: Output: inside: local outside: global

Tip: Functions can read global values, but to reassign a global from inside you must say global name first. Most of the time, though, it is cleaner to pass values in as parameters and return results out — avoid reaching for global.

Q. What does *args collect the extra positional arguments into?

Answer: *args gathers extra positional arguments into a tuple; **kwargs gathers extra keyword arguments into a dictionary.

✍️ Practice

  1. Write a function with a default parameter and call it both with and without that argument.
  2. Write a function that uses *args to multiply any number of values together.

🏠 Homework

  1. Write a function average(*numbers) that returns the average of however many numbers are passed in.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →