Functions are reusable blocks of code. Combined with modules, they are the primary way to organize Python programs.

Defining Functions

  def greet(name, greeting="Hello"):
    """Return a greeting message."""
    return f"{greeting}, {name}!"

print(greet("Alice"))
print(greet("Bob", "Hi"))
  

Parameters

Positional and Keyword Arguments

  def create_user(name, email, role="user", active=True):
    return {"name": name, "email": email, "role": role, "active": active}

create_user("Alice", "[email protected]")
create_user("Bob", "[email protected]", role="admin")
create_user(name="Charlie", email="[email protected]", active=False)
  

*args and **kwargs

  def sum_all(*args):
    return sum(args)

sum_all(1, 2, 3, 4)  # 10

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

print_info(name="Alice", age=30)

def combined(required, *args, **kwargs):
    print(f"Required: {required}")
    print(f"Extra args: {args}")
    print(f"Extra kwargs: {kwargs}")
  

Keyword-Only Arguments (Python 3+)

  def connect(host, port, *, timeout=30, ssl=True):
    ...

connect("localhost", 8080, timeout=60)  # ssl uses default
connect("localhost", 8080, 30)          # TypeError — timeout must be keyword
  

Return Values

  def min_max(numbers):
    return min(numbers), max(numbers)

lo, hi = min_max([3, 1, 4, 1, 5])

def find(items, target):
    for i, item in enumerate(items):
        if item == target:
            return i
    return -1  # not found
  

Functions without return implicitly return None.

Scope and Closures

  x = "global"

def outer():
    x = "outer"
    def inner():
        x = "inner"
        print(x)
    inner()
    print(x)

outer()
# inner
# outer
  

Closures capture variables from enclosing scope:

  def make_multiplier(factor):
    def multiply(x):
        return x * factor
    return multiply

double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5))  # 10
print(triple(5))  # 15
  

Lambda Functions

Anonymous one-expression functions:

  square = lambda x: x ** 2
sorted_names = sorted(["Charlie", "Alice", "Bob"], key=lambda n: len(n))

from functools import reduce
total = reduce(lambda acc, x: acc + x, [1, 2, 3, 4], 0)
  

Use lambdas for short callbacks. For anything complex, use def.

Recursion

  def factorial(n):
    if n <= 1:
        return 1
    return n * factorial(n - 1)

def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n - 1) + fibonacci(n - 2)
  

Python’s default recursion limit is 1000. Use iteration or functools.lru_cache for deep recursion.

Standard Library Highlights

  import math
math.sqrt(16)       # 4.0
math.ceil(3.2)      # 4
math.pi

import random
random.randint(1, 10)
random.choice(["a", "b", "c"])
random.shuffle(my_list)

from datetime import datetime, timedelta
now = datetime.now()
tomorrow = now + timedelta(days=1)

import json
data = json.loads('{"name": "Alice"}')
json.dumps(data, indent=2)
  

Type Annotations (Optional)

  def add(a: int, b: int) -> int:
    return a + b
  

See Type Hints for full coverage.

Best Practices

  1. One function, one job — keep functions focused
  2. Use descriptive namescalculate_total() not calc()
  3. Document with docstrings — explain parameters and return values
  4. Avoid mutable default arguments:
  # BAD
def append_to(item, lst=[]):
    lst.append(item)
    return lst

# GOOD
def append_to(item, lst=None):
    if lst is None:
        lst = []
    lst.append(item)
    return lst
  

Functions are the building blocks of every Python program. Next: Modules & Packages.