Python • 2026-10-10

Python Tutorial: Core Syntax, Functions, Files, and Error Handling

Learn Python fundamentals with practical examples covering variables, strings, collections, loops, functions, comprehensions, files, exceptions, classes, testing, and a command-line program.

Learn Python fundamentals with practical examples covering variables, strings, collections, loops, functions, comprehensions, files, exceptions, classes, testing, and a command-line program.

Developer workspace with code on a screen
Build your programming knowledge one practical example at a time.

Why Python?

Python is a general-purpose language used for automation, web services, data analysis, machine learning, scripting, and testing. Its readable syntax makes it a popular first language, but production-quality code still benefits from tests, type hints, clear structure, and careful error handling.

Strengths

  • Readable: indentation defines blocks, so code looks clean and consistent.
  • Batteries included: a large standard library for files, dates, JSON, networking, and more.
  • Huge ecosystem: libraries for web (Django, Flask, FastAPI), data (pandas, NumPy), and AI.
  • Versatile: scripts, apps, notebooks, and automation all work well.

Trade-offs

  • Slower than compiled languages for heavy CPU work, though libraries often use fast native code.
  • Dynamic typing can hide mistakes until runtime; type hints and tests help.

Setting Up: Running Code and Virtual Environments

  • Check your version with python --version (use a currently supported Python 3 release).
  • Run a script with python script.py, or experiment in the interactive REPL by typing python.
  • Use a virtual environment for each project so dependencies stay isolated.
  • Install packages with pip and record them in requirements.txt.

Creating a virtual environment and installing packages

python -m venv .venv

# macOS / Linux
source .venv/bin/activate
# Windows (PowerShell)
# .venv\Scripts\Activate.ps1

pip install requests pytest
pip freeze > requirements.txt
python hello.py

Variables, Numbers, and Strings

  • Variables are names that refer to objects; there is no declaration keyword.
  • Common types: int, float, bool, str, and None.
  • Use f-strings for readable formatting: f"Hello {name}".
  • Strings are immutable; methods such as .strip(), .split(), .replace(), and .upper() return new strings.
  • Use // for floor division, % for remainder, and ** for powers.

Variables and string formatting

name = "Asha"
age = 21
height = 1.65
is_student = True

print(f"{name} is {age} years old")
print(f"Height: {height:.1f} m")

text = "  Learn Python  "
print(text.strip().lower())               # learn python
print("a,b,c".split(","))                 # ['a', 'b', 'c']
print(7 // 2, 7 % 2, 2 ** 10)             # 3 1 1024
print(type(name), type(age), type(None))

Lists, Tuples, Sets, and Dictionaries

  • List: ordered and mutable, [1, 2, 3].
  • Tuple: ordered and immutable, (1, 2); good for fixed records.
  • Set: unordered unique items, {1, 2, 3}; fast membership tests.
  • Dictionary: key-value mapping, {"name": "Asha"}; fast lookup by key.
  • Slicing works on sequences: items[1:3], items[::-1].

Working with the core collections

fruits = ["apple", "banana", "cherry"]
fruits.append("mango")
print(fruits[0], fruits[-1], fruits[1:3])

point = (10, 20)
x, y = point                              # unpacking

unique = {1, 2, 2, 3, 3}                  # {1, 2, 3}
print(2 in unique)                        # True

student = {"name": "Asha", "grades": [90, 85]}
student["city"] = "Delhi"
print(student.get("age", "unknown"))      # safe lookup with default

for key, value in student.items():
    print(key, "->", value)

Conditions and Loops

  • if, elif, and else choose between branches.
  • for iterates over any iterable; use range() for numbers.
  • enumerate() gives index and value; zip() walks sequences together.
  • while repeats until a condition becomes false.
  • break exits a loop, continue skips to the next item.

Control flow patterns

score = 82
if score >= 90:
    grade = "A"
elif score >= 75:
    grade = "B"
else:
    grade = "C"
print(grade)

names = ["Asha", "Ravi", "Meera"]
marks = [90, 78, 85]
for i, (name, mark) in enumerate(zip(names, marks), start=1):
    print(f"{i}. {name}: {mark}")

total = 0
for n in range(1, 6):
    if n == 4:
        continue
    total += n
print(total)                              # 11

Functions, Arguments, and Type Hints

  • Define reusable behavior with def and return values with return.
  • Use default arguments, keyword arguments, *args, and **kwargs.
  • Never use a mutable default like []; use None and create the list inside.
  • Add type hints and docstrings to document intent.
  • Keep functions focused on one job and avoid hidden global state.

Well-formed functions

def average(numbers: list[float]) -> float:
    """Return the mean of a non-empty list."""
    if not numbers:
        raise ValueError("numbers must not be empty")
    return sum(numbers) / len(numbers)


def greet(name: str, greeting: str = "Hello") -> str:
    return f"{greeting}, {name}!"


def add_item(item, items=None):
    items = [] if items is None else items   # avoids shared mutable default
    items.append(item)
    return items


def summary(*args, **kwargs):
    return len(args), kwargs


print(average([90, 80, 70]))              # 80.0
print(greet("Asha", greeting="Hi"))

Comprehensions, Lambdas, and Useful Built-ins

  • List, set, and dictionary comprehensions build collections in one readable line.
  • Generator expressions compute lazily and save memory.
  • sorted(..., key=...), min, max, sum, any, and all solve many tasks without loops.
  • Keep comprehensions short; switch to a loop if it becomes hard to read.

Comprehensions and built-ins

numbers = [1, 2, 3, 4, 5, 6]

squares = [n * n for n in numbers]
evens = [n for n in numbers if n % 2 == 0]
lookup = {n: n * n for n in numbers}
unique_lengths = {len(w) for w in ["a", "bb", "cc"]}

total = sum(n * n for n in numbers)        # generator expression

people = [("Asha", 21), ("Ravi", 19), ("Meera", 24)]
youngest_first = sorted(people, key=lambda p: p[1])
print(any(age < 20 for _, age in people))  # True
print(squares, evens, total)

Working with Files and JSON

  • Use pathlib.Path for cross-platform paths.
  • Use a with statement so files are always closed properly.
  • Always specify encoding="utf-8" for text files.
  • The json module reads and writes structured data.

Reading, writing, and JSON

from pathlib import Path
import json

notes = Path("notes.txt")
notes.write_text("first line\nsecond line\n", encoding="utf-8")

with notes.open(encoding="utf-8") as f:
    for line in f:
        print(line.rstrip())

data = {"name": "Asha", "skills": ["Python", "SQL"]}
Path("profile.json").write_text(json.dumps(data, indent=2), encoding="utf-8")

loaded = json.loads(Path("profile.json").read_text(encoding="utf-8"))
print(loaded["skills"])

Exceptions and Error Handling

  • Use try/except for failures you can handle meaningfully.
  • Catch specific exceptions; avoid bare except: that hides bugs.
  • else runs when no exception occurred; finally always runs for cleanup.
  • Raise your own exceptions with clear messages, or define custom exception classes.

Handling and raising exceptions

class ValidationError(Exception):
    """Raised when user input is invalid."""


def parse_age(text: str) -> int:
    try:
        age = int(text)
    except ValueError:
        raise ValidationError(f"Age must be a number, got {text!r}") from None
    if not 0 <= age <= 120:
        raise ValidationError("Age must be between 0 and 120")
    return age


try:
    print(parse_age("abc"))
except ValidationError as error:
    print("Invalid input:", error)
else:
    print("Parsed successfully")
finally:
    print("Done")

Classes and Dataclasses

  • Classes bundle data and behavior; __init__ sets up each instance.
  • Use @dataclass for simple data-holding classes: it generates __init__, __repr__, and __eq__.
  • Prefer composition over deep inheritance.
  • Name internal attributes with a leading underscore by convention.

A class and a dataclass

from dataclasses import dataclass, field


class BankAccount:
    def __init__(self, owner: str, balance: float = 0.0):
        self.owner = owner
        self._balance = balance

    def deposit(self, amount: float) -> None:
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    @property
    def balance(self) -> float:
        return self._balance


@dataclass
class Student:
    name: str
    grades: list[int] = field(default_factory=list)

    @property
    def average(self) -> float:
        return sum(self.grades) / len(self.grades) if self.grades else 0.0


s = Student("Asha", [90, 85, 95])
print(s, s.average)

Modules, Packages, and Testing

  • Any .py file is a module; import with import or from ... import ....
  • Guard script entry points with if __name__ == "__main__":.
  • Use pytest to write small, fast tests for your functions.
  • Write tests for normal cases, edge cases, and expected errors.

A function and its pytest tests

# calculator.py
def divide(a: float, b: float) -> float:
    if b == 0:
        raise ZeroDivisionError("b must not be zero")
    return a / b


# test_calculator.py
import pytest
from calculator import divide


def test_divide_normal():
    assert divide(10, 2) == 5


def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        divide(1, 0)

# Run with:  pytest -q

Best Practices

  • Follow PEP 8 style and use a formatter such as Black or Ruff.
  • Use virtual environments and pin dependencies.
  • Add type hints and check them with mypy or pyright.
  • Prefer pathlib, f-strings, and context managers.
  • Write tests as you go, and handle errors explicitly.
  • Never hard-code secrets; read them from environment variables.
  • Use logging instead of print in real applications.

Complete Example: A Word-Frequency Command-Line Tool

This program counts words in a text file and reports the most common ones. It combines functions, type hints, pathlib, argparse, and error handling using only the standard library.

Word counter CLI

from __future__ import annotations

import argparse
import re
from collections import Counter
from pathlib import Path


def count_words(file_path: Path) -> Counter[str]:
    """Return a frequency count for words in a UTF-8 text file."""
    text = file_path.read_text(encoding="utf-8")
    words = re.findall(r"\b[\w']+\b", text.lower())
    return Counter(words)


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Count the most common words in a file.")
    parser.add_argument("path", type=Path, help="Path to a UTF-8 text file")
    parser.add_argument("-n", "--top", type=int, default=10, help="How many words to show")
    return parser.parse_args()


def main() -> int:
    args = parse_args()

    try:
        counts = count_words(args.path)
    except FileNotFoundError:
        print(f"File not found: {args.path}")
        return 1
    except UnicodeDecodeError:
        print("The file is not valid UTF-8 text.")
        return 1

    for word, frequency in counts.most_common(args.top):
        print(f"{word:<15} {frequency}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Running the tool

python wordcount.py notes.txt --top 5

Frequently Asked Questions

Is Python good for beginners?

Yes. Its readable syntax makes it approachable, and it is useful in many practical areas such as automation, web, and data.

What is a virtual environment?

It isolates a project's installed packages so different projects can use different dependency versions without conflicts.

Should I learn frameworks immediately?

First get comfortable with functions, collections, modules, exceptions, files, and basic testing. Then choose a framework for a specific project.

What is the difference between a list and a tuple?

Lists are mutable and tuples are immutable. Use tuples for fixed groups of values and lists for collections that change.

What is a list comprehension?

It is a compact way to build a list from an iterable, optionally with a condition, such as [n * n for n in numbers if n > 0].

Why use a with statement for files?

It guarantees the file is closed properly even if an error occurs, which prevents resource leaks.

← Back to All Articles