📊 Section 1 · Foundations 🟢 Beginner MODULE 03

Python Refresher for Data Scientists

⏱️ 24 min read
📖 Theory + Code
🧩 5 Quiz Questions
🏗️ 1 Challenge
Your progress in Section 143%
🎯 What you'll learn: A fast, focused review of lists, dictionaries, and comprehensions; functions and lambda functions; and the specific idioms — unpacking, *args/**kwargs, and f-strings — that show up constantly once you start writing real data science code.

Why This Refresher Matters

NumPy, Pandas, and Scikit-Learn are all just Python libraries — every method you call and every result you get back is still built from ordinary Python: lists, dicts, functions, loops. The data science libraries lean especially hard on a handful of Python features, so it's worth making sure they're second nature before moving on.

💡
This is a refresher, not a first introduction
If any of this lesson feels completely unfamiliar rather than "oh right, I remember this," it's worth working through BitWithBite's Python Mastery course first — this lesson moves quickly and assumes you've written basic Python before.

Lists — Quick Review

A list is an ordered, mutable collection. You'll use lists constantly — as the raw material you turn into NumPy arrays and Pandas columns.

lists_review.py
PYTHON
# Creating and indexing
scores = [88, 92, 75, 61, 99]
print(scores[0])     # 88 — first item
print(scores[-1])    # 99 — last item

# Slicing
print(scores[1:3])   # [92, 75] — index 1 up to (not including) 3
print(scores[:2])    # [88, 92] — first two
print(scores[::-1])  # reversed copy of the list

# Common methods
scores.append(70)      # add to the end
scores.sort()          # sort in place, ascending
print(len(scores))   # number of items
print(sum(scores))   # total of all items
print(max(scores), min(scores))

List comprehensions

A list comprehension builds a new list from an existing iterable in a single, readable line. You'll see this pattern everywhere in data cleaning code.

list_comprehensions.py
PYTHON
# The classic pattern: [expression for item in iterable]
nums = [1, 2, 3, 4, 5]
squares = [n ** 2 for n in nums]
print(squares)  # [1, 4, 9, 16, 25]

# Adding a filter condition
evens = [n for n in nums if n % 2 == 0]
print(evens)   # [2, 4]

# Cleaning a messy list of names in one line
raw_names = ["  Alice", "BOB  ", "charlie"]
clean_names = [name.strip().title() for name in raw_names]
print(clean_names)  # ['Alice', 'Bob', 'Charlie']
When to reach for a comprehension
A comprehension is a great fit when you're transforming or filtering a list in one clear step. If the logic needs several lines or multiple conditions to stay readable, a regular for loop is usually the better choice.

Dictionaries — Quick Review

A dict maps keys to values. In data science, dicts show up as configuration, as lookup tables, and as the shape JSON data naturally takes once you load it.

dicts_review.py
PYTHON
# Creating and accessing
player = {"name": "Amara", "team": "Falcons", "goals": 14}
print(player["name"])        # "Amara"
print(player.get("assists", 0))  # 0 — safe default if key is missing

# Adding / updating
player["assists"] = 6
player["goals"] += 1

# Looping over a dict
for key, value in player.items():
    print(f"{key}: {value}")

# Dict comprehension — build a lookup table in one line
players = ["Amara", "Ben", "Cleo"]
name_lengths = {name: len(name) for name in players}
print(name_lengths)  # {'Amara': 5, 'Ben': 3, 'Cleo': 4}
⚠️
Use .get() to avoid crashes
Accessing a missing key with player["assists"] raises a KeyError and stops your program. player.get("assists", 0) returns a safe default instead — invaluable when working with real-world data where fields are sometimes missing.

Functions

Functions package up repeatable logic — cleaning steps, calculations, formatting — so you can name a piece of work once and reuse it everywhere.

functions_review.py
PYTHON
def celsius_to_fahrenheit(celsius):
    return celsius * 9 / 5 + 32

print(celsius_to_fahrenheit(100))  # 212.0

# A default argument
def normalize_score(score, max_score=100):
    return score / max_score

print(normalize_score(45))       # 0.45 — uses default max_score
print(normalize_score(9, 10))    # 0.9 — overrides the default

Lambda Functions

A lambda is a small, anonymous, one-expression function. On its own it isn't especially interesting — but it's the idiom you'll reach for constantly when sorting data or passing a quick transformation into a Pandas method like .apply().

lambdas.py
PYTHON
# A regular function...
def square(n):
    return n * n

# ...vs. the same thing as a lambda
square_lambda = lambda n: n * n
print(square_lambda(5))  # 25

# Where lambdas actually shine: sorting by a custom key
players = [{"name": "Amara", "goals": 14}, {"name": "Ben", "goals": 9}, {"name": "Cleo", "goals": 21}]

top_scorers = sorted(players, key=lambda p: p["goals"], reverse=True)
print(top_scorers[0]["name"])  # "Cleo" — the top scorer

# A preview of how this looks with pandas (covered in Section 2)
# df["goals_doubled"] = df["goals"].apply(lambda g: g * 2)
Rule of thumb
If a lambda needs more than one line of logic to explain, write a regular def function instead and give it a name. Lambdas are for small, throwaway expressions — usually passed directly as an argument to another function.

Unpacking, *args, and **kwargs

These three idioms come up constantly once you start reading other people's data science code, even if you don't write them yourself every day.

Unpacking

unpacking.py
PYTHON
# Unpacking a list into named variables
point = [10, 20]
x, y = point
print(x, y)  # 10 20

# Skipping values with an underscore, and "gathering the rest" with *
scores = [88, 92, 75, 61]
first, *rest = scores
print(first)  # 88
print(rest)   # [92, 75, 61]

# Unpacking is exactly what makes this pattern from Section 3 work:
# for key, value in player.items():

*args and **kwargs (briefly)

A function that needs to accept a flexible number of arguments uses *args (extra positional arguments, collected into a tuple) and **kwargs (extra keyword arguments, collected into a dict). You'll mostly recognize these in library function signatures rather than write them yourself early on.

args_kwargs.py
PYTHON
def summarize(*args, **kwargs):
    print("positional:", args)
    print("keyword:", kwargs)

summarize(88, 92, 75, source="quiz", graded=True)
# positional: (88, 92, 75)
# keyword: {'source': 'quiz', 'graded': True}

f-strings for Formatting Output

An f-string embeds expressions directly inside a string, prefixed with f. It's the standard way to build readable output — summaries, log lines, chart titles — in modern Python.

fstrings.py
PYTHON
name = "Amara"
goals = 14
avg = 2.3333

# Basic embedding
print(f"{name} scored {goals} goals this season.")

# Formatting numbers: 2 decimal places
print(f"Average per game: {avg:.2f}")  # "Average per game: 2.33"

# Padding and alignment — handy for lining up a quick text table
for n, g in [("Amara", 14), ("Ben", 9)]:
    print(f"{n:<10} {g:>3}")
# Amara      14
# Ben         9
📝
Why f-strings beat string concatenation
"Score: " + str(score) works, but breaks the moment a value isn't already a string, and it gets unreadable fast with several values. f"Score: {score}" handles the conversion for you and stays readable no matter how many values you add.

Lesson Summary

Let's recap everything you learned in this lesson:

List comprehensions[expr for item in iterable if condition] — transform or filter a list in one line.
Dict comprehensions follow the same pattern and build lookup tables quickly. Use .get(key, default) to avoid KeyError.
Lambda functions are small anonymous functions, most useful as a key= argument to sorted() or inside .apply().
Unpacking assigns list/tuple items to named variables in one line; *args/**kwargs let a function accept a flexible number of arguments.
f-strings (f"...{value}...") are the standard, readable way to build formatted output.
🧩 Knowledge Check — Lesson 3
Answer all 5 questions to test your understanding. Instant feedback on every answer.
1. What does [n * 2 for n in [1, 2, 3]] evaluate to?
2. Why use my_dict.get("key", 0) instead of my_dict["key"]?
3. What is a lambda function most commonly used for in data science code?
4. After first, *rest = [10, 20, 30, 40], what is rest?
5. Which of these correctly formats a number to 2 decimal places inside an f-string?
💪
Coding Challenge — Lesson 3
Apply what you learned · Beginner Level

Combine several idioms from this lesson into one small script.

Challenge: Quiz Score Report 📋

You're given a list of dicts, each representing one student's quiz score out of 20. Write a script that: (1) uses a list comprehension to build a list of each student's percentage score, (2) uses sorted() with a lambda key to find the top scorer, and (3) prints a formatted report using f-strings.

students = [
  {"name": "Zara", "score": 18},
  {"name": "Malik", "score": 15},
  {"name": "Priya", "score": 20},
]

Rules: Percentage = score / 20 * 100. Print each student's name and percentage (2 decimal places), then print a final line announcing the top scorer.
💡 Show hints if you're stuck
  • Comprehension: [s["score"] / 20 * 100 for s in students]
  • Top scorer: sorted(students, key=lambda s: s["score"], reverse=True)[0]
  • f-string: f"{s['name']}: {pct:.2f}%"
Finished this lesson?
Mark it complete to track your progress.
🎉

Lesson 3 Complete!

Your Python fundamentals are sharp and ready for data work. Next up: reading and writing the file formats you'll meet in almost every real dataset — CSV, JSON, and Excel.

Module 03 of 7 Section 1 — Python for Data Science Foundations