def

Updated March 15, 2026 · Keywords
keyword function definition

The def keyword defines a function in Python. Functions are reusable blocks of code that perform a specific task. They help you organize code, avoid repetition, and build modular programs.

Syntax

def function_name(parameters):
    """Optional docstring describing the function."""
    # Function body
    return result  # Optional

Basic Examples

A Simple Function

def greet():
    print("Hello, World!")

greet()
# Hello, World!

Function with Parameters

def greet(name):
    print(f"Hello, {name}!")

greet("Alice")
# Hello, Alice!

Function with Return Value

def add(a, b):
    return a + b

result = add(3, 5)
print(result)
# 8

Default Parameters

You can provide default values for parameters:

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

print(greet("Alice"))
# Hello, Alice!

print(greet("Bob", "Hi"))
# Hi, Bob!

Keyword Arguments

Pass arguments by name for clarity and flexibility:

def describe_pet(animal, name):
    return f"I have a {animal} named {name}."

# Positional arguments
print(describe_pet("dog", "Buddy"))
# I have a dog named Buddy.

# Keyword arguments
print(describe_pet(name="Whiskers", animal="cat"))
# I have a cat named Whiskers.

*args and **kwargs

Variable Positional Arguments

def sum_all(*args):
    total = 0
    for num in args:
        total += num
    return total

print(sum_all(1, 2, 3))
# 6

print(sum_all(10, 20, 30, 40))
# 100

Variable Keyword Arguments

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

print_info(name="Alice", age=30, city="NYC")
# name: Alice
# age: 30
# city: NYC

Combining All Types

def func(a, b, *args, default="value", **kwargs):
    print(f"a={a}, b={b}")
    print(f"args={args}")
    print(f"default={default}")
    print(f"kwargs={kwargs}")

func(1, 2, 3, 4, default="custom", x=10, y=20)
# a=1, b=2
# args=(3, 4)
# default=custom
# kwargs={x: 10, y: 20}

Type Hints

Python 3.5+ supports type hints:

def greet(name: str) -> str:
    return f"Hello, {name}!"

def process_numbers(numbers: list[int]) -> int:
    return sum(numbers)

# With None and Optional
from typing import Optional

def find_user(user_id: int, default: Optional[str] = None) -> str:
    return default or "Guest"

Docstrings

Document your functions with docstrings:

def calculate_area(width, height):
    """Calculate the area of a rectangle.
    
    Args:
        width: The width of the rectangle.
        height: The height of the rectangle.
    
    Returns:
        The area (width * height).
    """
    return width * height

Common docstring styles:

  • Google style
  • NumPy style
  • Sphinx (reST) style

Nested Functions

Functions can be defined inside other functions:

def outer():
    x = "outer"
    
    def inner():
        print(f"Inner sees: {x}")
    
    inner()

outer()
# Inner sees: outer

Closures

Inner functions that capture variables from their enclosing scope:

def make_multiplier(factor):
    def multiplier(number):
        return number * factor
    return multiplier

double = make_multiplier(2)
triple = make_multiplier(3)

print(double(5))
# 10

print(triple(5))
# 15

Lambda Functions

Anonymous inline functions:

# Full function
def square(x):
    return x ** 2

# Lambda equivalent
square = lambda x: x ** 2

print(square(4))
# 16

# Common use cases
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
sorted_nums = sorted(numbers, key=lambda x: -x)
print(sorted_nums)
# [9, 6, 5, 4, 3, 2, 1, 1]

Generator Functions

Use yield to create generators:

def count_up_to(n):
    """Generator that yields numbers from 1 to n."""
    count = 1
    while count <= n:
        yield count
        count += 1

for num in count_up_to(5):
    print(num)
# 1
# 2
# 3
# 4
# 5

Best Practices

Name Functions Clearly

# Bad
def do_stuff(x):
    return x * 2

# Good
def calculate_double(value):
    return value * 2

Keep Functions Short

Each function should do one thing well:

# Bad
def process_user_data(user):
    # Validate, save, and send email...
    
# Good
def validate_user(user):
    ...

def save_user(user):
    ...

def send_welcome_email(user):
    ...

Use Default Arguments Carefully

Avoid mutable defaults:

# Bad - default list persists across calls
def add_item(item, items=[]):
    items.append(item)
    return items

# Good - use None
def add_item(item, items=None):
    if items is None:
        items = []
    items.append(item)
    return items

See Also