Python Basics — Lists, Dictionaries and Functions
The four data structures and the one language feature that every later lesson assumes you already know. This is the load-bearing lesson of the whole track.
Lesson 1 gave you one value at a time. That gets old fast — real programs handle collections: fifteen video titles, a hundred student scores, every word in a file.
This lesson is the load-bearing one. Everything after it assumes you know what is here.
#Lists — ordered, changeable, your default
A list holds things in order, and you can change it after the fact.
tracks = ["Python", "PHP", "Ruby", "C#", "Racket"]
print(tracks[0]) # Python — counting starts at zero
print(tracks[2]) # Ruby
print(tracks[-1]) # Racket — negative counts from the end
print(len(tracks)) # 5tracks[-1] is the single most useful indexing trick in Python. You never have to write tracks[len(tracks) - 1] again.
#Changing a list
tracks.append("JavaScript") # add to the end
tracks.insert(0, "Scratch") # add at a position
tracks.remove("C#") # delete by value
last = tracks.pop() # remove and return the final item
tracks[1] = "Python 3" # overwrite in place#Slicing
A slice takes a range: list[start:stop], where stop is not included.
nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
nums[2:5] # [2, 3, 4] — stops before index 5
nums[:3] # [0, 1, 2] — from the start
nums[7:] # [7, 8, 9] — to the end
nums[::2] # [0, 2, 4, 6, 8] — every second item
nums[::-1] # [9, 8, ..., 0] — reversednums[::-1] reverses a list in one expression. It works on strings too: "hello"[::-1] gives 'olleh'.
#Dictionaries — look things up by name
Lists answer "what is at position 3?" Dictionaries answer "what is the value for this key?" — which is usually the question you actually have.
video = {
"title": "Intro to Python",
"duration": "10:47",
"views": 197,
"track": "python",
}
print(video["title"]) # Intro to Python
video["views"] = 198 # update
video["published"] = 2020 # add a new keyAsking for a key that does not exist raises KeyError and stops your program. When a key might be missing, use .get():
video.get("likes") # None — no crash
video.get("likes", 0) # 0 — supply your own defaultLooping over a dictionary gives you keys by default. Almost always, what you want is .items():
for key, value in video.items():
print(f"{key:>10}: {value}") title: Intro to Python
duration: 10:47
views: 198
track: python
published: 2020The :>10 right-aligns each key in a ten-character column. Small touch, much more readable output.
#Nesting is where it gets useful
Real data is a list of dictionaries:
catalogue = [
{"title": "Intro to Python", "views": 197, "track": "python"},
{"title": "PHP Web Form", "views": 259, "track": "php"},
{"title": "Racket Download and Install", "views": 696, "track": "racket"},
]
for item in catalogue:
print(f"{item['title']} — {item['views']} views")
total = sum(item["views"] for item in catalogue)
print(f"Total: {total} views")#Tuples — a list that cannot change
Same syntax as a list, but with parentheses, and permanently frozen once created.
dimensions = (1920, 1080)
width, height = dimensions # unpackingUse a tuple when the number of items is part of the meaning: a coordinate pair, an RGB colour, a row from a database. If you would be alarmed to find a third item in there, it should be a tuple.
Tuples can also be dictionary keys. Lists cannot.
#Sets — unique things, fast membership tests
tags = {"python", "beginner", "python", "loops"}
print(tags) # {'python', 'beginner', 'loops'} — duplicate goneTwo superpowers:
Deduplicating. list(set(items)) removes duplicates in one pass.
Speed. Checking x in some_set is effectively instant no matter how large the set is. Checking x in some_list scans every element. On a list of a million items, that difference is the difference between a program that finishes and one that does not.
Sets also do maths:
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a & b # {3, 4} intersection — in both
a | b # {1,2,3,4,5,6} union — in either
a - b # {1, 2} difference — in a, not in bRemember &. It turns a whole class of interview problems into a one-liner — including the Intersection of Two Arrays problem later in this course.
#Picking the right one
| You need | Use | Why |
|---|---|---|
| An ordered collection you will change | list | The default. Reach for it first. |
| Lookup by name | dict | Instant access by key |
| A fixed group that belongs together | tuple | Cannot be modified by accident |
| Unique items, or fast "is it in here?" | set | Deduplicates, and searches instantly |
#Functions — name a block of work
A function packages up code so you can run it again without re-typing it.
def greet(name):
return f"Hello, {name}!"
message = greet("Dom")
print(message) # Hello, Dom!defstarts the definition.- Whatever is in the parentheses are the parameters — the inputs.
returnhands a value back to whoever called it.
#Default values and keyword arguments
def format_duration(seconds, show_hours=False):
minutes, secs = divmod(seconds, 60)
if show_hours:
hours, minutes = divmod(minutes, 60)
return f"{hours}:{minutes:02d}:{secs:02d}"
return f"{minutes}:{secs:02d}"
format_duration(647) # '10:47'
format_duration(647, show_hours=True) # '0:10:47'divmod(a, b) returns the quotient and remainder together — exactly what you want for time conversion. {secs:02d} pads with a leading zero so you get 10:07, not 10:7.
Naming the argument at the call site (show_hours=True) makes the code self-documenting. Compare format_duration(647, True) — true what?
#A function should do one thing
def average(numbers):
if not numbers:
return 0
return sum(numbers) / len(numbers)
def summarise(scores):
return {
"count": len(scores),
"average": round(average(scores), 1),
"best": max(scores),
"worst": min(scores),
}
print(summarise([88, 92, 79, 95, 84])){'count': 5, 'average': 87.6, 'best': 95, 'worst': 79}average guards against an empty list — without that check, len(numbers) is zero and you get ZeroDivisionError. Handling the empty case is the difference between code that works on your test data and code that works.
Note if not numbers: rather than if len(numbers) == 0:. In Python, empty collections are falsy. Same for "", 0, and None. It reads better and it is the idiom you will see everywhere.
Check yourself
4 questions on what you just read. No score is kept — it is only here to catch the bits that did not stick.
-
1What does
nums[2:5]return fromnums = [0,1,2,3,4,5,6]?Show the answer
A.
[2, 3, 4]The stop index is excluded. That is exactly what makes
nums[:3]andnums[3:]split a list with no overlap and nothing missing. -
2A key might not exist in a dictionary. Which access will not crash?
Show the answer
A.
video.get("likes", 0)Square brackets raise
KeyErrorand stop the program..get()returnsNone, or whatever default you pass. -
3You need to check membership against a million items, repeatedly. What should hold them?
Show the answer
A. A
set— lookups are effectively instant regardless of size.x in some_listscans every element.x in some_sethashes straight to the answer. On large data that is the difference between finishing and not. -
4What is wrong with a function that only
prints its result?Show the answer
A. The caller cannot use the value for anything.
printshows a value to a human;returnhands it back to your program. A function that only prints is a dead end.
#Key takeaways
- Lists are ordered and changeable; index from
0, or from-1at the end. - Slices exclude the stop index — that is what makes
a[:n]anda[n:]split cleanly. - Dictionaries map keys to values. Use
.get()when a key might be missing,.items()when looping. - Sets deduplicate and test membership instantly.
&,|and-do real set maths. - Functions
returnvalues;printis only for humans. Guard your edge cases.
Next: loops, and the comprehension syntax that replaces most of them.
Finished this one?
The challenge above has no posted solution — that is deliberate. Get it working, then come back for the next lesson.