Python Functions and Data Structures: Lists, Tuples, Dicts, and Sets
Once variables, data types, conditionals, and loops feel familiar, the next step is writing cleaner code. That is where functions and data structures become useful.
Functions let you name a piece of logic. Data structures let you store multiple values in the right shape. When both are used well, a messy script can become a program that is easier to read, test, and extend.
Read the distinction this way: a function answers “what work should happen?”, while a data structure answers “what kind of values are we storing?”. Do not choose a structure because its name sounds advanced. Choose based on whether order, mutation, keys, or uniqueness matters for the problem.
How to think before writing code
Before writing a function, describe its input and output. For example, calculate_total(items) accepts a collection of prices and returns one number. A boundary like this makes the function easier to test because we know what can go in and what should come out.
Keep return and print() separate. return hands data back to the calling code, while print() only displays text in the terminal. A function that returns a value is more flexible because its result can be calculated with, compared, stored, or sent to a UI.
For a data structure, ask three questions: does the data have an order, does it need to change, and how will you find one value? A list fits an ordered collection that can change. A tuple fits a small group of values that should not be modified. A dictionary fits values with named keys. A set fits values where order does not matter and duplicates should disappear.
Quick recap
In the previous article, we wrote code like this:
# File: 01-recap-grade-checker.py
name = input("Name: ")
score = int(input("Score: "))
if score >= 80:
result = "pass"
else:
result = "review again"
print(f"{name}: {result}")
This works, but if the grading logic is needed in multiple places, we would repeat the same condition again and again. Functions help remove that repetition.
Defining functions with def
Create a function with def.
# File: 02-get-result.py
def get_result(score):
if score >= 80:
return "pass"
return "review again"
print(get_result(85))
print(get_result(60))
return sends a value out of the function. Once return runs, the function is done.
A good function name explains the purpose. get_result() is clearer than process() when the job is to determine a score result.
Parameters and return values
Parameters are inputs to a function.
# File: 03-greet.py
def greet(name):
return f"Hello, {name}"
message = greet("Ayu")
print(message)
A function can have more than one parameter:
# File: 04-calculate-total.py
def calculate_total(price, quantity):
return price * quantity
total = calculate_total(25_000, 3)
print(total)
Use a return value when the result needs to be reused. Use print() only when you actually want to display something to the user.
Default parameters
Default parameters are useful when a value is often the same.
# File: 05-default-parameter.py
def format_price(amount, currency="IDR"):
return f"{currency} {amount}"
print(format_price(50000))
print(format_price(50000, "USD"))
Avoid mutable defaults such as empty lists. It looks harmless, but it can create surprising bugs.
# File: 06-safe-list-default.py
def add_item(item, items=None):
if items is None:
items = []
items.append(item)
return items
This None pattern is safer for default values that should become lists or dictionaries.
Simple scope
A variable created inside a function is a local variable.
# File: 07-local-scope.py
def calculate_tax(price):
tax = price * 0.1
return tax
print(calculate_tax(100_000))
The tax variable only exists inside the function. That is a good thing because the function does not pollute the rest of the program.
Avoid changing global variables from inside functions too often. A function is easier to read when it receives input through parameters and sends output through return.
Lists for ordered collections
Use a list when you have multiple items and their order matters.
# File: 08-list-indexing.py
scores = [80, 90, 85]
print(scores[0])
print(scores[-1])
Indexes start at 0. scores[0] gets the first item, while scores[-1] gets the last item.
Add an item with append():
# File: 09-list-append.py
scores.append(95)
print(scores)
Process all items with a loop:
# File: 10-loop-list.py
for score in scores:
print(score)
Tuples for values that should not change
A tuple is similar to a list, but its contents cannot be changed after creation.
# File: 11-tuple-coordinate.py
coordinate = (-6.2, 106.8)
latitude = coordinate[0]
longitude = coordinate[1]
print(latitude, longitude)
Use tuples for small values that should stay fixed, such as coordinates, value pairs, or function results that should not be modified.
Dictionaries for key-value data
A dictionary stores data as key-value pairs.
# File: 12-student-dictionary.py
student = {
"name": "Ayu",
"scores": [80, 90, 85],
"active": True,
}
print(student["name"])
print(student["scores"])
Dictionaries are great for representing simple objects: users, products, orders, configuration, and so on.
To avoid an error when a key may not exist, use get():
# File: 13-dictionary-get.py
nickname = student.get("nickname", "-")
print(nickname)
Sets for unique values
A set stores unique values and works well for membership checks.
# File: 14-unique-tags.py
tags = {"python", "beginner", "python"}
print(tags)
Even though "python" appears twice, the set stores it once.
Sets are also fast for checking whether a value exists:
# File: 15-membership-check.py
allowed_roles = {"admin", "editor"}
if "admin" in allowed_roles:
print("allowed")
Use a set when order does not matter and uniqueness does.
List comprehensions
A list comprehension is a compact way to create a new list from an old one.
# File: 16-passed-scores.py
scores = [60, 75, 90, 85]
passed_scores = [score for score in scores if score >= 80]
print(passed_scores)
Use this carefully. If the expression gets too long, a normal loop is usually easier to read.
Mini project: student averages
Now combine functions, lists, and dictionaries.
# File: 17-average-student.py
def calculate_average(scores):
total = sum(scores)
return total / len(scores)
student = {
"name": "Ayu",
"scores": [80, 90, 85],
}
average = calculate_average(student["scores"])
print(student["name"], average)
A version with multiple students:
# File: 18-average-students.py
def calculate_average(scores):
if len(scores) == 0:
return 0
return sum(scores) / len(scores)
students = [
{"name": "Ayu", "scores": [80, 90, 85]},
{"name": "Budi", "scores": [70, 75, 78]},
{"name": "Citra", "scores": [95, 92, 90]},
]
for student in students:
average = calculate_average(student["scores"])
print(f"{student['name']}: {average}")
Notice how the function keeps the average calculation from being repeated.
Which data structure should you choose?
Use a list when you have an ordered collection of items that may change.
Use a tuple when you have a small ordered value that should not change.
Use a dictionary when named fields matter, such as name, email, or price.
Use a set when values must be unique or you often check whether an item exists.
The right structure makes code simpler. If you are unsure, start with a list or dictionary and refactor when the need becomes clearer.
Common mistakes
The first mistake is writing a function that does too much.
# File: 19-too-much-work.py
def process_everything():
# read user input
# calculate total
# save file
# print report
pass
It is usually better to split this into smaller functions: read_input(), calculate_total(), save_data(), and print_report().
The second mistake is using a mutable default argument.
# File: 20-mutable-default-bug.py
def add_score(score, scores=[]):
scores.append(score)
return scores
Use None as the default, then create a new list inside the function.
The third mistake is using dictionaries for everything. Dictionaries are flexible, but once data starts having behavior, classes and dataclasses may be a better fit.
Mini exercises
Try building:
- A
calculate_total(items)function that receives a list of prices and returns the total. - An
is_even(number)function that returnsTruefor even numbers. - A dictionary for product data: name, price, and stock.
- A set for unique tags from several articles.
- A list comprehension that selects numbers above 50 from a list.
Type the examples yourself. Do not only read them. Python starts to click when you watch data move from one shape to another.
Checklist before moving on
Before moving to files, explain your program without looking at the code. Which function receives input? Which function returns a result? Which values are stored as a list, dictionary, or set, and why?
Test a few boundaries too: an empty list, one item, duplicate values, and an input with the wrong type. If a function fails on an empty case, decide what you want it to do, such as return 0, return an empty list, or raise a clear error. There is no universal answer; the important part is making the decision intentionally and applying it consistently.
Next: files, errors, and modules
After functions and data structures, you are ready to build scripts that read files, handle errors, and split code across multiple files. Continue with Python Files, Errors, and Modules.
If you want to review the previous foundation, revisit Python Fundamentals: Variables, Types, and Control Flow.
Related Articles
Keep reading within the same topic.
Python Fundamentals: Variables, Types, and Control Flow
Learn Python fundamentals from scratch: variables, data types, operators, input/output, if statements, loops, and a simple practice script.
Python Fundamentals Learning Path: Series Recap and Next Steps
A practical recap of the Python Fundamentals series, with six lessons, key skills, checkpoints, and clear next steps after building the CLI project.
Python CLI Mini Project: Build an Expense Tracker from Scratch
Build a Python CLI expense tracker from scratch using input, JSON file storage, functions, dataclasses, error handling, and basic testing.
Python Project Structure: venv, pip, Requirements, and Testing
Learn how to organize a Python project with folders, virtual environments, pip, requirements.txt, .env files, pytest, and a simple entry point.