Skip to content
OrionStats
Module 6 · Python Basics · Core language

Python from zero — values, collections, control flow, functions, files and errors

Everything an analyst needs from core Python before pandas — explained with business examples and real outputs.

Beginner90 min
Recommended first

This lesson builds on: Aggregations, GROUP BY and HAVING.

Learning objectives
  • Use variables and the core data types (int, float, str, bool)
  • Choose between lists, tuples, dictionaries and sets
  • Control flow with if/elif/else, for and while loops
  • Write functions with parameters and return values; import modules
  • Read and write files safely and handle errors with try/except

Setting up

Install Python 3.11+ (python.org or Anaconda) and use VS Code or Jupyter notebooks. Every code block below was executed; the box underneath shows its real output. You can paste blocks into a notebook and run them yourself — download the Orion CSV files from the Datasets page into a folder and run your notebook from that folder.

Variables and data types

A variable is a name that refers to a value. Python infers the type from the value.

store = "Baku Central"      # str (text)
orders = 1250               # int (whole number)
avg_order_value = 89.95     # float (decimal)
is_open = True              # bool (True/False)

revenue = orders * avg_order_value
print(type(store), type(orders), type(avg_order_value), type(is_open))
print("Revenue:", revenue)
print(f"{store} revenue: {revenue:,.2f}")
<class 'str'> <class 'int'> <class 'float'> <class 'bool'>
Revenue: 112437.5
Baku Central revenue: 112,437.50
  • # starts a comment.
  • An f-string (f"...") inserts values; :,.2f formats with thousands separators and 2 decimals.
  • Floats are binary approximations — for money in production code use decimal.Decimal or integer cents.
print(0.1 + 0.2)
print(round(0.1 + 0.2, 2))
print(7 / 2, 7 // 2, 7 % 2, 2 ** 10)
0.30000000000000004
0.3
3.5 3 1 1024

/ is true division, // integer division, % remainder, ** power.

Strings

city = "  baku  "
print(city.strip().title())
email = "aylin.aliyev12@example.com"
print(email.split("@")[1])
print(email.upper().startswith("AYLIN"), len(email))
print("Orion" + "Stats", "-" * 10)
Baku
example.com
True 26
OrionStats ----------

Strings are immutable: methods return new strings. Indexing starts at 0; email[0:5] is a slice of characters 0–4.

Lists, tuples, dictionaries and sets

Type Example Ordered Mutable Typical use
list [120, 95, 143] yes yes sequence of values
tuple ("Baku", "East") yes no fixed record, dict keys
dict {"Baku": 621, "Ganja": 794} insertion order yes key → value lookup
set {"card", "cash"} no yes unique values, membership
monthly_orders = [868, 682, 1120, 938]
monthly_orders.append(1109)
print(monthly_orders, len(monthly_orders), sum(monthly_orders), max(monthly_orders))
print(monthly_orders[0], monthly_orders[-1], monthly_orders[1:3])

store_region = {"Baku Central": "East", "Ganja": "West"}
store_region["Tbilisi"] = "West"
print(store_region["Ganja"], store_region.get("Ankara", "unknown"))
for name, region in store_region.items():
    print(name, "->", region)

methods = ["card", "cash", "card", "wallet", "card"]
print(set(methods), "bnpl" in set(methods))
[868, 682, 1120, 938, 1109] 5 4717 1120
868 1109 [682, 1120]
West unknown
Baku Central -> East
Ganja -> West
Tbilisi -> West
{'card', 'cash', 'wallet'} False

.get(key, default) avoids a KeyError when the key is missing. Sets remove duplicates instantly — a quick way to count distinct values.

Conditions

def classify(attainment):
    if attainment >= 1.0:
        return "On or above plan"
    elif attainment >= 0.95:
        return "Slightly below plan"
    else:
        return "Below plan"

for a in [1.107, 0.968, 0.947]:
    print(a, classify(a))
1.107 On or above plan
0.968 Slightly below plan
0.947 Below plan

Indentation (4 spaces) defines blocks — it is syntax, not style. Like SQL's CASE, the first true branch wins.

Loops

targets = {"Sumqayit": 71900, "Ankara": 124900, "Tbilisi": 103200}
actuals = {"Sumqayit": 79626, "Ankara": 118288, "Tbilisi": 99884}

below = []
for store, plan in targets.items():
    pct = actuals[store] / plan
    if pct < 1:
        below.append(store)
    print(f"{store:<10} {pct:6.1%}")
print("Below plan:", below)

# list comprehension: build a list in one expression
pct_list = [round(actuals[s] / targets[s] * 100, 1) for s in targets]
print(pct_list)
Sumqayit   110.7%
Ankara      94.7%
Tbilisi     96.8%
Below plan: ['Ankara', 'Tbilisi']
[110.7, 94.7, 96.8]

These figures are the real 2025 plan and actuals for three Orion stores. while loops repeat until a condition is false — rarely needed in analysis, where you loop over data or (better) use pandas vectorised operations.

Functions and modules

A function packages logic so it can be reused and tested:

def net_revenue(quantity, unit_price, discount_pct=0.0):
    """Net revenue of an order line."""
    if quantity < 0 or not 0 <= discount_pct < 1:
        raise ValueError("invalid quantity or discount")
    return quantity * unit_price * (1 - discount_pct)

print(net_revenue(2, 50))
print(net_revenue(2, 50, discount_pct=0.2))

import math, statistics
from datetime import date, timedelta
print(math.sqrt(144), statistics.median([3, 1, 4, 1, 5]))
print(date(2025, 3, 1) - timedelta(days=1))
100.0
80.0
12.0 3
2025-02-28
  • Parameters can have defaults (discount_pct=0.0); call with keyword arguments for clarity.
  • The docstring documents the function.
  • import loads modules from the standard library (math, statistics, datetime, csv, json, pathlib) or installed packages (pandas, numpy).

Files

import csv
from pathlib import Path

path = Path("retail/stores.csv")
with path.open(newline="", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))
print(len(rows), "stores")
print(rows[0]["store_name"], rows[0]["city"])

out = Path("/tmp/open_stores.txt")
with out.open("w", encoding="utf-8") as f:
    for r in rows:
        if not r["close_date"]:
            f.write(r["store_name"] + "\n")
print(out.read_text().splitlines()[:3])
9 stores
Orion Baku Central Baku
['Orion Baku Central', 'Orion Baku Riverside', 'Orion Ganja']

with closes the file automatically, even if an error occurs. pathlib.Path handles paths on Windows, macOS and Linux.

Error handling

def safe_ratio(a, b):
    try:
        return a / b
    except ZeroDivisionError:
        return None
    except TypeError as e:
        print("Bad input:", e)
        return None

print(safe_ratio(10, 4), safe_ratio(10, 0), safe_ratio("10", 2))
Bad input: unsupported operand type(s) for /: 'str' and 'int'
2.5 None None

Catch specific exceptions you can handle; let unexpected ones surface. A bare except: that hides all errors is the Python version of IFERROR(…, 0).

Raise early, with a clear message

In data pipelines, raise ValueError(f"Missing column {col} in {file}") at the point of failure saves hours compared with a cryptic error ten steps later.

Practice

What does 7 // 2 evaluate to in Python?

Log in to answer and get feedback

regions = {"Baku": "East"}. What does regions.get("Ankara", "unknown") return?

Log in to answer and get feedback

Given m = [868, 682, 1120, 938, 1109], what is m[-1]? (Enter the number.)

Log in to answer and get feedback

Why is a bare except: that returns 0 on any error a bad idea in a data pipeline?

Log in to answer and get feedback

Summary

  • Core types: int, float, str, bool; format with f-strings; beware float rounding.
  • Lists (ordered), tuples (fixed), dicts (lookup), sets (unique).
  • if/elif/else, for loops and comprehensions control flow; indentation is syntax.
  • Functions with defaults and docstrings; modules via import.
  • Use with for files and catch specific exceptions.