{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Python — Next Steps\n",
    "\n",
    "Picks up where **`python_basics.ipynb`** left off. Four short topics that take you from \"I can read Python\" to \"I can write small useful scripts.\"\n",
    "\n",
    "**Sections**\n",
    "1. Modules — using code other people wrote\n",
    "2. File I/O — reading and writing text files\n",
    "3. Error handling — `try` / `except`\n",
    "4. List comprehensions — building lists in one line\n",
    "5. Mini exercises\n",
    "\n",
    "Run cells with **Shift+Enter**. Break things, fix them, learn more."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Modules\n",
    "\n",
    "Python ships with hundreds of **modules** — collections of useful functions you can `import`. Two everyday ones: `math` and `random`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "\n",
    "print(math.sqrt(16))      # 4.0\n",
    "print(math.pi)            # 3.1415...\n",
    "print(math.floor(3.7))    # 3\n",
    "print(math.ceil(3.2))     # 4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import random\n",
    "\n",
    "print(random.random())                   # float between 0 and 1\n",
    "print(random.randint(1, 10))             # integer 1..10 inclusive\n",
    "print(random.choice([\"mango\", \"durian\", \"longan\"]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Three import styles — pick whichever reads best."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import math                  # use as math.sqrt(...)\n",
    "from math import sqrt        # use as sqrt(...) directly\n",
    "import math as m             # use as m.sqrt(...)\n",
    "\n",
    "print(math.sqrt(9), sqrt(9), m.sqrt(9))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Some modules ship with Python (the **standard library** — `math`, `random`, `json`, `os`, `datetime`...). Others you install with `pip install name`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. File I/O\n",
    "\n",
    "Reading and writing text files. The `with` statement makes sure the file is properly closed even if something goes wrong."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Write a small file\n",
    "with open(\"notes.txt\", \"w\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"Chiang Mai\\n\")\n",
    "    f.write(\"Lampang\\n\")\n",
    "    f.write(\"Chiang Rai\\n\")\n",
    "\n",
    "print(\"Wrote notes.txt\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Read it back, whole file at once\n",
    "with open(\"notes.txt\", encoding=\"utf-8\") as f:\n",
    "    text = f.read()\n",
    "\n",
    "print(text)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Or read line by line (better for large files)\n",
    "with open(\"notes.txt\", encoding=\"utf-8\") as f:\n",
    "    for line in f:\n",
    "        print(line.strip())   # .strip() removes the trailing newline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**File modes** — the second argument to `open()`:\n",
    "\n",
    "| Mode | Meaning                              |\n",
    "|------|--------------------------------------|\n",
    "| `\"r\"` | read (default)                      |\n",
    "| `\"w\"` | write — **erases the file first**   |\n",
    "| `\"a\"` | append — adds to the end             |"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Append a fourth city\n",
    "with open(\"notes.txt\", \"a\", encoding=\"utf-8\") as f:\n",
    "    f.write(\"Mae Hong Son\\n\")\n",
    "\n",
    "with open(\"notes.txt\", encoding=\"utf-8\") as f:\n",
    "    print(f.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Error handling\n",
    "\n",
    "When something goes wrong, Python **raises an exception** and the program stops. Run the cell below — it will fail on purpose."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# This will raise ZeroDivisionError\n",
    "result = 10 / 0\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Read the **traceback** above carefully — the last line names the exception type (`ZeroDivisionError`). To handle it gracefully, wrap the risky code in `try` / `except`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    result = 10 / 0\n",
    "    print(result)\n",
    "except ZeroDivisionError:\n",
    "    print(\"Cannot divide by zero!\")\n",
    "\n",
    "print(\"Program keeps running\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Catch different exceptions differently. Use `as e` to inspect the exception object."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "student = {\"name\": \"Ploy\", \"age\": 12}\n",
    "\n",
    "try:\n",
    "    print(student[\"score\"])           # missing key\n",
    "except KeyError as e:\n",
    "    print(f\"No such key: {e}\")\n",
    "except Exception as e:\n",
    "    print(f\"Some other problem: {e}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`finally` always runs — useful for cleanup. You can also `raise` your own exceptions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def divide(a, b):\n",
    "    if b == 0:\n",
    "        raise ValueError(\"b must not be zero\")\n",
    "    return a / b\n",
    "\n",
    "try:\n",
    "    print(divide(10, 2))\n",
    "    print(divide(10, 0))\n",
    "except ValueError as e:\n",
    "    print(f\"Caught: {e}\")\n",
    "finally:\n",
    "    print(\"This runs no matter what\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Rule of thumb:** only catch exceptions you actually know how to handle. Catching `Exception` and silently ignoring it hides real bugs."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. List comprehensions\n",
    "\n",
    "A concise way to build a list. Read it as: \"the *expression* for each *item* in *iterable*\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# The long way\n",
    "squares = []\n",
    "for n in range(10):\n",
    "    squares.append(n * n)\n",
    "print(squares)\n",
    "\n",
    "# The comprehension way\n",
    "squares = [n * n for n in range(10)]\n",
    "print(squares)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Add an `if` to filter:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "evens = [n for n in range(20) if n % 2 == 0]\n",
    "print(evens)\n",
    "\n",
    "cities = [\"Chiang Mai\", \"Lampang\", \"Mae Hong Son\", \"Pai\"]\n",
    "short = [c for c in cities if len(c) < 8]\n",
    "print(short)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The same syntax works for **dict** and **set** comprehensions."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Dict: map city -> name length\n",
    "lengths = {c: len(c) for c in cities}\n",
    "print(lengths)\n",
    "\n",
    "# Set: unique first letters\n",
    "letters = {c[0] for c in cities}\n",
    "print(letters)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**When *not* to use them:** if the expression or condition is long, write a normal `for` loop. Comprehensions are for clarity; if yours needs a comment to read, it's gone too far."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Mini exercises\n",
    "\n",
    "1. Use `random.choice` to pick a random city from `[\"Chiang Mai\", \"Lampang\", \"Pai\", \"Mae Hong Son\"]`, then print it 5 times (so you see the randomness).\n",
    "2. Write a function `safe_int(s)` that returns `int(s)` if it works, or `None` if the string isn't a valid integer. (Hint: `ValueError`.)\n",
    "3. Given `prices = [45, 120, 30, 200, 75]`, use a list comprehension to build a list of prices in USD assuming 1 USD = 35 THB. Round to 2 decimals."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 1: your code here\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 2: your code here\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Exercise 3: your code here\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "### Solutions"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 1\n",
    "import random\n",
    "cities = [\"Chiang Mai\", \"Lampang\", \"Pai\", \"Mae Hong Son\"]\n",
    "for _ in range(5):\n",
    "    print(random.choice(cities))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 2\n",
    "def safe_int(s):\n",
    "    try:\n",
    "        return int(s)\n",
    "    except ValueError:\n",
    "        return None\n",
    "\n",
    "print(safe_int(\"42\"))      # 42\n",
    "print(safe_int(\"hello\"))   # None\n",
    "print(safe_int(\"3.14\"))    # None (int() rejects decimals in strings)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 3\n",
    "prices = [45, 120, 30, 200, 75]\n",
    "in_usd = [round(p / 35, 2) for p in prices]\n",
    "print(in_usd)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Where to go after this\n",
    "\n",
    "- **classes** — `class Dog: ...` for grouping data + behavior together\n",
    "- **generators** — `yield` for lazy sequences that don't fit in memory\n",
    "- **decorators** — `@something` for wrapping functions\n",
    "- **type hints** — `def greet(name: str) -> str:` for better tooling and clarity\n",
    "- **`pathlib`** — modern file-path handling that beats `os.path`\n",
    "- **`json`** — read/write JSON files in two lines\n",
    "- **`requests`** (pip install) — fetch web pages and APIs"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
