Python Functions: How to Define and Use Them
Functions are the building blocks of readable, maintainable Python code. Instead of writing the same logic over and over, you wrap it in a function once and call it wherever you need it. In this guide you'll learn how to define functions, pass data in through parameters, get data back with return values, and use Python's powerful shorthand tools like lambda.
Why Functions? The DRY Principle
DRY stands for Don't Repeat Yourself. If you find yourself copying and pasting the same block of code in multiple places, that's a sign it should be a function. Functions make your code shorter, easier to read, and much simpler to fix β change the function once and every call site benefits.
Defining a Function
Use the def keyword, followed by the function name, parentheses, and a colon. The indented block beneath it is the function body.
def greet():
print("Welcome to GrowBit!")
# Call the function
greet() # Welcome to GrowBit!
Indentation matters in Python β the standard is 4 spaces. Every line at the same indentation level is part of the same block. A function ends when indentation returns to the previous level.
Parameters and Arguments
Parameters are the variable names inside the function definition. Arguments are the actual values you pass when you call the function.
def plant(crop, x, y):
print(f"Planting {crop} at ({x}, {y})")
# Positional arguments β order matters
plant("wheat", 0, 0) # Planting wheat at (0, 0)
plant("carrot", 3, 2) # Planting carrot at (3, 2)
# Keyword arguments β order doesn't matter
plant(y=4, crop="tomato", x=1) # Planting tomato at (1, 4)
Default Parameter Values
You can give parameters a default value so callers don't have to provide them every time. Default parameters must come after non-default ones.
def water_plot(x, y, amount=1.0):
print(f"Watering ({x},{y}) with {amount}L")
water_plot(0, 0) # uses default: 1.0L
water_plot(2, 3, 2.5) # overrides: 2.5L
water_plot(1, 1, amount=0.5) # keyword override
Return Values
Use return to send a value back to the caller. Without an explicit return, a function returns None.
def calculate_profit(cost, sell_value):
return sell_value - cost
profit = calculate_profit(10, 35)
print(f"Profit: {profit} gold") # Profit: 25 gold
# Return multiple values (Python returns a tuple)
def crop_stats(name, cost, value):
profit = value - cost
roi = (profit / cost) * 100
return profit, roi
p, r = crop_stats("wheat", 10, 25)
print(f"Profit: {p}, ROI: {r:.1f}%") # Profit: 15, ROI: 150.0%
Variable Scope
Variables created inside a function are local β they only exist while the function runs. Variables created outside are global. A function can read a global variable, but to modify it you need the global keyword (which is generally a code smell β prefer returning values instead).
gold = 100 # global
def buy_seed(cost):
# Local variable β disappears after function returns
new_balance = gold - cost
return new_balance
remaining = buy_seed(15)
print(remaining) # 85
print(gold) # still 100 β global unchanged
# Using global (avoid when possible)
def earn_gold(amount):
global gold
gold += amount
earn_gold(50)
print(gold) # 150
global. Pass values in as arguments and return the updated value β it keeps functions predictable and testable.
Lambda Functions
A lambda is a compact, anonymous function defined in a single expression. Use it when you need a short throwaway function β especially as an argument to sorted(), map(), or filter().
# Regular function
def double(x):
return x * 2
# Equivalent lambda
double = lambda x: x * 2
# Most useful as an inline key function
crops = [
{"name": "wheat", "value": 25},
{"name": "carrot", "value": 15},
{"name": "tomato", "value": 40},
]
sorted_crops = sorted(crops, key=lambda c: c["value"], reverse=True)
for c in sorted_crops:
print(c["name"], c["value"])
# tomato 40 / wheat 25 / carrot 15
Built-in Functions Worth Knowing
Python ships with dozens of ready-made functions. Here are the ones you'll use constantly:
len(x)β number of items in a list, string, or dictrange(start, stop, step)β generates a sequence of integersprint(*args)β output to the consoletype(x)β returns the type of a valuesorted(iterable, key=..., reverse=False)β returns a sorted listsum(iterable),min(iterable),max(iterable)abs(x)β absolute valueround(x, ndigits)β round a float
Practical: Farming Functions
Here's a small set of functions that power a GrowBit-style farm simulation. Notice how each function does one thing, takes clear inputs, and returns a value instead of mutating globals.
def plant(crop, x, y, grid):
"""Place a crop dict onto the grid at position (x, y)."""
grid[y][x] = {"name": crop, "days": 0, "mature": False}
print(f"Planted {crop} at ({x},{y})")
def advance_day(grid, growth_days=5):
"""Age every planted crop by one day; mark mature when ready."""
for row in grid:
for cell in row:
if cell is not None:
cell["days"] += 1
if cell["days"] >= growth_days:
cell["mature"] = True
def harvest(x, y, grid, sell_value=25, cost=10):
"""Remove a mature crop and return the profit."""
cell = grid[y][x]
if cell and cell["mature"]:
grid[y][x] = None
return sell_value - cost
return 0 # not ready yet