Learn Python fundamentals with practical examples covering variables, strings, collections, loops, functions, comprehensions, files, exceptions, classes, testing, and a command-line program.
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 typingpython. - Use a virtual environment for each project so dependencies stay isolated.
- Install packages with
pipand record them inrequirements.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.pyVariables, Numbers, and Strings
- Variables are names that refer to objects; there is no declaration keyword.
- Common types:
int,float,bool,str, andNone. - 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, andelsechoose between branches.foriterates over any iterable; userange()for numbers.enumerate()gives index and value;zip()walks sequences together.whilerepeats until a condition becomes false.breakexits a loop,continueskips 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) # 11Functions, Arguments, and Type Hints
- Define reusable behavior with
defand return values withreturn. - Use default arguments, keyword arguments,
*args, and**kwargs. - Never use a mutable default like
[]; useNoneand 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, andallsolve 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.Pathfor cross-platform paths. - Use a
withstatement so files are always closed properly. - Always specify
encoding="utf-8"for text files. - The
jsonmodule 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/exceptfor failures you can handle meaningfully. - Catch specific exceptions; avoid bare
except:that hides bugs. elseruns when no exception occurred;finallyalways 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
@dataclassfor 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
.pyfile is a module; import withimportorfrom ... import .... - Guard script entry points with
if __name__ == "__main__":. - Use
pytestto 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 -qBest 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
logginginstead ofprintin 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 5Frequently 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.