Functions

Python Fundamentals — Functions
Python Fundamentals
Course 1 · Chapter 8 · Functions

🧩 Functions

Everything so far has been one script running top to bottom. Functions let you package up reusable pieces of logic — this chapter covers defining and calling them, the four ways to pass parameters (positional, keyword, default, and variadic), return, and how variable scope actually works inside a function body.

🛠️ Defining and Calling Functions

def greet(name): return f"Hello, {name}!" message = greet("Ada") print(message) # Hello, Ada!

Like everything else in Python, the function body is defined by indentation, not braces — the same rule that's applied to every if, for, and while block since Chapter 3.

📥 Parameters: Positional, Keyword, and Default

def describe_pet(name, animal="dog"): # animal has a default value return f"{name} is a {animal}" print(describe_pet("Rex")) # Rex is a dog — positional, uses default print(describe_pet("Whiskers", "cat")) # Whiskers is a cat — positional print(describe_pet(name="Rex", animal="cat")) # keyword arguments — order doesn't matter print(describe_pet(animal="parrot", name="Polly"))

Calling with name=... instead of relying on position is a keyword argument — it makes the call self-documenting and lets you skip default parameters you don't need to override, or reorder arguments freely.

📦 *args and **kwargs

Sometimes you don't know in advance how many arguments a function will receive. *args collects extra positional arguments into a tuple; **kwargs collects extra keyword arguments into a dictionary:

def total(*args): return sum(args) print(total(1, 2, 3, 4)) # 10 — args is (1, 2, 3, 4) def build_profile(**kwargs): return kwargs print(build_profile(name="Ada", age=28)) # {'name': 'Ada', 'age': 28} — kwargs is a dict

The names args and kwargs are convention, not a language rule — * and ** are what actually matter. You'll see *args, **kwargs constantly in real Python code, especially in functions designed to wrap or forward calls to other functions.

↩️ Return Values

def no_return(): print("did something") # no return statement result = no_return() print(result) # None — a function with no return implicitly returns None def min_max(numbers): return min(numbers), max(numbers) # actually returns a tuple low, high = min_max([3, 7, 1, 9]) # unpacked, same as Chapter 6 print(low, high) # 1 9

"Returning multiple values" is really just returning a single tuple that gets unpacked at the call site — the same tuple mechanics from Chapter 6, not a separate language feature.

🔍 Scope

count = 10 # a global variable def increment(): count = 20 # this creates a NEW local variable, doesn't touch the global one print(count) # 20 increment() print(count) # 10 — unchanged def increment_global(): global count count += 1 # now this DOES modify the global increment_global() print(count) # 11
⚠ Mutable Default Arguments Are a Classic Trap
def add_item(item, basket=[]): # DANGER: default list created ONCE, not per-call basket.append(item) return basket print(add_item("apple")) # ['apple'] print(add_item("banana")) # ['apple', 'banana'] — NOT a fresh empty list!

A default value like basket=[] is evaluated exactly once, when the function is defined — not fresh on every call. Since lists are mutable (Chapter 6), every call that relies on the default ends up sharing and mutating the same list. The standard fix is basket=None, then if basket is None: basket = [] inside the function body.

Function Parameters: Go vs Kotlin vs Python

FeatureGoKotlinPython
Default parameter valuesNot supported — simulate with variadic params or a config structSupported directly, same idea as PythonSupported directly
Keyword/named argumentsNot supportedSupported directly (named arguments)Supported directly
Variadic parametersfunc f(nums ...int)fun f(vararg nums: Int)def f(*args)

Kotlin's default and named parameters map almost one-to-one onto Python's — this is one of the friendlier corners of the language to pick up coming from Kotlin. Go's lack of both is a real, common source of friction going the other direction.

def / return

Defines a function; a missing return implicitly returns None.

Default & keyword args

def f(x, y=default); call with f(y=1, x=2) in any order.

*args / **kwargs

Collect extra positional args into a tuple, extra keyword args into a dict.

Scope

Assigning inside a function creates a local variable by default; global opts out of that.

💻 Coding Challenges

Challenge 1: Flexible Greeting

Write a function greet(name, greeting="Hello") that returns f"{greeting}, {name}!". Call it three ways: with just a name, with both arguments positionally, and with both arguments as keyword arguments in reversed order.

Goal: Get comfortable with default values and keyword-argument calling in practice.

→ Solution

Challenge 2: Variadic Average

Write a function average(*args) that returns the average of however many numbers it's called with. Call it with 2 numbers, then again with 5 numbers.

Goal: Practice writing and calling a function with *args.

→ Solution

Challenge 3: Fix the Mutable Default Bug

Given the buggy add_item(item, basket=[]) function from this chapter's warning box, rewrite it correctly using the basket=None pattern so that each call without a basket argument gets its own fresh, independent list.

Goal: Apply the fix for the mutable-default-argument gotcha directly, not just recognize it.

→ Solution

🎯 What's Next

Next chapter: Modules & Packagesimport, pip, and virtual environments (venv).