Data Types, Decisions, and Loops

This notebook covers the core building blocks of Python: data structures, conditional logic, and iteration. Every example uses golf data.

What You’ll Learn

  • Lists, tuples, and dictionaries — when to use each
  • Boolean logic and comparison operators
  • if / elif / else decision structures
  • for and while loops
  • Putting it all together to analyze a round of golf

Concept: Organizing Data

Programs need to store collections of data. Python has three main built-in collection types:

Type Syntax Mutable? Use Case
List [4, 3, 5] Yes Ordered, changeable collections (hole scores, shot distances)
Tuple (4, 385, 7) No Fixed data that shouldn’t change (par, yardage, handicap index)
Dict {"driver": 245} Yes Key-value lookups (club distances, player stats)

The key insight: use tuples for data that describes something fixed (a hole’s par never changes mid-round), lists for data that accumulates (scores build up as you play), and dicts for lookups (what club did I hit on hole 5?).


Code: Lists

Lists are ordered, mutable collections. You’ll use them constantly.

# Scores for a 9-hole round
scores = [4, 5, 3, 6, 4, 3, 5, 4, 5]

# Accessing elements (0-indexed)
print(f"Hole 1 score: {scores[0]}")
print(f"Last hole score: {scores[-1]}")

# Slicing
front_3 = scores[:3]
back_3 = scores[-3:]
print(f"First 3 holes: {front_3}")
print(f"Last 3 holes: {back_3}")
# Lists are mutable — you can change them
scores = [4, 5, 3, 6, 4, 3, 5, 4, 5]

# Oops, scored a 5 on hole 2, not 6
scores[3] = 5
print(f"Corrected scores: {scores}")

# Add a score (hole 10 if we keep playing)
scores.append(4)
print(f"After adding hole 10: {scores}")

# Useful list operations
print(f"Total: {sum(scores)}")
print(f"Holes played: {len(scores)}")
print(f"Best hole: {min(scores)}")
print(f"Worst hole: {max(scores)}")
print(f"Sorted: {sorted(scores)}")

Code: Tuples

Tuples are like lists but immutable — once created, they can’t be changed. Use them for fixed data.

# A hole's characteristics never change during a round
# (hole_number, par, yardage, handicap_index)
hole_1 = (1, 4, 385, 7)
hole_2 = (2, 3, 165, 13)
hole_3 = (3, 5, 530, 3)

# Access by index
print(f"Hole {hole_1[0]}: Par {hole_1[1]}, {hole_1[2]} yards")

# Tuple unpacking — much more readable
hole_num, par, yardage, hcp_idx = hole_3
print(f"Hole {hole_num}: Par {par}, {yardage} yards (HCP {hcp_idx})")
# Tuples are immutable — this will raise an error
hole_1 = (1, 4, 385, 7)

try:
    hole_1[1] = 5  # Can't change par!
except TypeError as e:
    print(f"Error: {e}")
    print("Tuples can't be modified — that's the point!")
# A list of tuples — common pattern for course data
course_holes = [
    (1, 4, 385, 7),
    (2, 3, 165, 13),
    (3, 5, 530, 3),
    (4, 4, 410, 1),
    (5, 4, 370, 11),
    (6, 3, 195, 15),
    (7, 5, 545, 5),
    (8, 4, 420, 9),
    (9, 4, 395, 17),
]

total_par = sum(par for _, par, _, _ in course_holes)
total_yardage = sum(yds for _, _, yds, _ in course_holes)
print(f"Front 9: Par {total_par}, {total_yardage} yards")

Code: Dictionaries

Dictionaries map keys to values. Perfect for lookups and structured data.

# Club distances (average carry in yards)
club_distances = {
    "Driver": 245,
    "3-Wood": 215,
    "4-Hybrid": 195,
    "5-Iron": 180,
    "6-Iron": 170,
    "7-Iron": 155,
    "8-Iron": 140,
    "9-Iron": 125,
    "PW": 110,
    "50°": 95,
    "58°": 70,
}

# Lookup
print(f"7-Iron carries {club_distances['7-Iron']} yards")

# Check if a key exists
print(f"Has a 60° wedge? {'60°' in club_distances}")

# Safe lookup with .get()
distance = club_distances.get("60°", "unknown")
print(f"60° distance: {distance}")
# A player's round as a dict
round_data = {
    "player": "Bear Woods",
    "course": "North Park Golf Course",
    "date": "2024-03-15",
    "scores": [4, 3, 5, 5, 4, 3, 6, 4, 4, 5, 3, 5, 5, 4, 3, 5, 4, 5],
    "weather": "sunny",
}

print(f"{round_data['player']} at {round_data['course']}")
print(f"Total: {sum(round_data['scores'])}")
print(f"Weather: {round_data['weather']}")

Concept: Making Decisions

Programs need to make choices. In golf terms: - Is this score a birdie, par, or bogey? - Did the player make the cut? - Should I hit driver or lay up?

Python uses if, elif, and else for decision logic, combined with boolean expressions that evaluate to True or False.

Code: Boolean Expressions and Comparisons

score = 3
par = 4

# Comparison operators
print(f"{score} == {par}{score == par}")   # Equal
print(f"{score} != {par}{score != par}")   # Not equal
print(f"{score} < {par}{score < par}")    # Less than
print(f"{score} <= {par}{score <= par}")   # Less than or equal
print(f"{score} > {par}{score > par}")    # Greater than
# Combining with and, or, not
score = 68
par = 72
cut_line = 144  # 36-hole cut
day1_score = 70
day2_score = 73
two_day_total = day1_score + day2_score

under_par = score < par
made_cut = two_day_total <= cut_line

print(f"Under par today? {under_par}")
print(f"Made the cut? {made_cut}")
print(f"Under par AND made cut? {under_par and made_cut}")
print(f"Under par OR made cut? {under_par or made_cut}")
print(f"Missed cut? {not made_cut}")

Code: if / elif / else

def scoring_name(score: int, par: int) -> str:
    """Return the golf name for a score on a hole."""
    diff = score - par

    if diff <= -3:
        return "Albatross"
    elif diff == -2:
        return "Eagle"
    elif diff == -1:
        return "Birdie"
    elif diff == 0:
        return "Par"
    elif diff == 1:
        return "Bogey"
    elif diff == 2:
        return "Double Bogey"
    else:
        return "Triple Bogey+"


# Test it
test_cases = [(2, 4), (3, 4), (4, 4), (5, 4), (6, 4), (3, 5), (1, 3)]
for score, par in test_cases:
    print(f"Score {score} on a par {par} = {scoring_name(score, par)}")
# Nested decisions: club selection based on distance and conditions
distance_to_pin = 165
wind = "into"  # "into", "with", "none"
lie = "fairway"  # "fairway", "rough", "sand"

if lie == "sand":
    club = "SW"
elif distance_to_pin > 200:
    club = "3-Wood"
elif distance_to_pin > 150:
    if wind == "into":
        club = "5-Iron"  # Club up for headwind
    elif wind == "with":
        club = "7-Iron"  # Club down for tailwind
    else:
        club = "6-Iron"
else:
    club = "8-Iron"

print(f"{distance_to_pin} yards, {lie} lie, wind {wind}{club}")

Concept: Iteration (Loops)

Loops let you repeat an action for each item in a collection or until a condition is met.

  • for loop — iterate over a known collection (holes, scores, players)
  • while loop — repeat until a condition changes (keep putting until holed out)

In data science, for loops are far more common. You’ll loop over rows, columns, files, and datasets constantly.

Code: for Loops

# Loop over scores to build a hole-by-hole summary
pars   = [4, 3, 5, 4, 4, 3, 5, 4, 4]
scores = [4, 3, 6, 5, 4, 2, 5, 4, 5]

for i in range(len(scores)):
    name = scoring_name(scores[i], pars[i])
    print(f"Hole {i + 1}: {scores[i]} ({name})")
# Better: use enumerate() for index + value
for i, (score, par) in enumerate(zip(scores, pars), start=1):
    name = scoring_name(score, par)
    diff = score - par
    diff_str = f"{diff:+d}" if diff != 0 else " E"
    print(f"Hole {i:2d}: {score} (par {par}) {diff_str:>3s}  {name}")
# Loop over a list of tuples — course data
course_holes = [
    (1, 4, 385, 7),  (2, 3, 165, 13), (3, 5, 530, 3),
    (4, 4, 410, 1),  (5, 4, 370, 11), (6, 3, 195, 15),
    (7, 5, 545, 5),  (8, 4, 420, 9),  (9, 4, 395, 17),
]

par_3s = []
par_4s = []
par_5s = []

for hole_num, par, yardage, hcp in course_holes:
    if par == 3:
        par_3s.append(yardage)
    elif par == 4:
        par_4s.append(yardage)
    else:
        par_5s.append(yardage)

print(f"Par 3s: {par_3s} (avg {sum(par_3s)/len(par_3s):.0f} yards)")
print(f"Par 4s: {par_4s} (avg {sum(par_4s)/len(par_4s):.0f} yards)")
print(f"Par 5s: {par_5s} (avg {sum(par_5s)/len(par_5s):.0f} yards)")
# Loop over a dictionary
club_distances = {
    "Driver": 245, "3-Wood": 215, "5-Iron": 180,
    "7-Iron": 155, "9-Iron": 125, "PW": 110,
}

print("Clubs that carry over 150 yards:")
for club, distance in club_distances.items():
    if distance > 150:
        print(f"  {club}: {distance} yards")

Code: while Loops

# Simulate putting until holed out
import random
random.seed(42)

distance_feet = 30  # Starting putt distance
putt_count = 0

while distance_feet > 0:
    putt_count += 1

    # Simulate: leave it roughly 20% of the remaining distance
    remaining = distance_feet * random.uniform(0.05, 0.35)

    if remaining < 1.0:  # Inside 1 foot = holed
        print(f"  Putt {putt_count}: {distance_feet:.1f} ft → Holed!")
        distance_feet = 0
    else:
        print(f"  Putt {putt_count}: {distance_feet:.1f} ft → {remaining:.1f} ft remaining")
        distance_feet = remaining

print(f"Total putts: {putt_count}")

Code: Putting It All Together

Let’s combine everything to build a scorecard display.

# Complete scorecard builder
course_holes = [
    (1, 4, 385), (2, 3, 165), (3, 5, 530), (4, 4, 410), (5, 4, 370),
    (6, 3, 195), (7, 5, 545), (8, 4, 420), (9, 4, 395),
]
scores = [4, 3, 6, 5, 4, 2, 5, 4, 5]

print(f"{'Hole':>4s}  {'Par':>3s}  {'Yds':>4s}  {'Score':>5s}  {'vs Par':>6s}  {'Name'}")
print("-" * 45)

running_total = 0
running_par = 0

for (hole_num, par, yardage), score in zip(course_holes, scores):
    running_total += score
    running_par += par
    diff = score - par
    diff_str = f"{diff:+d}" if diff != 0 else "E"
    name = scoring_name(score, par)
    print(f"{hole_num:4d}  {par:3d}  {yardage:4d}  {score:5d}  {diff_str:>6s}  {name}")

print("-" * 45)
total_diff = running_total - running_par
total_str = f"{total_diff:+d}" if total_diff != 0 else "E"
print(f"{'OUT':>4s}  {running_par:3d}        {running_total:5d}  {total_str:>6s}")

AI: Decision Logic and Loops

Exercise 1: Let AI Build a Scorecard

Try this prompt:

“Write Python code that takes a list of pars and a list of scores for 18 holes. Print a formatted scorecard that shows each hole, par, score, the golf name (birdie, par, bogey, etc.), and a running total. Include front 9 and back 9 subtotals.”

Evaluate: - Does it handle the front 9 / back 9 split correctly? - Does it handle eagles and double bogeys? - How does the formatting compare to what we built above?

Exercise 2: Have AI Explain Code

Copy this line from our code above and ask AI to explain it:

for i, (score, par) in enumerate(zip(scores, pars), start=1):

“Explain this Python line step by step. What does zip do? What does enumerate do? What does the tuple unpacking (score, par) do?”

This is one of the most valuable uses of AI — explaining unfamiliar syntax. It’s faster than searching docs and you can ask follow-up questions.

Exercise 3: Strategy Decision Tree

Try asking AI to build something more complex:

“Write a Python function that recommends a club based on: distance to pin (yards), lie (fairway/rough/sand), wind (into/with/none), and elevation change (uphill/downhill/flat). Use if/elif/else logic. Include type hints.”

Evaluate: Does the logic make golf sense? Would a real golfer agree with the recommendations?

# Paste and test AI-generated code here

Summary

  • Lists [4, 3, 5] — mutable, ordered; use for scores, distances, any changing collection
  • Tuples (4, 385, 7) — immutable; use for fixed data like hole info (par, yardage)
  • Dicts {"Driver": 245} — key-value pairs; use for lookups and structured data
  • if/elif/else — make decisions based on conditions
  • for loops — iterate over collections; enumerate() and zip() are your friends
  • while loops — repeat until a condition changes

Next up: Functions — building reusable golf tools.

Get the Complete Course Bundle

All notebooks, the full golf dataset, and new tutorials — straight to your inbox.