Back to blog

Python Phase 1: Fundamentals - Getting Started with Python

pythonprogrammingfundamentalsbeginnerstutorial
Python Phase 1: Fundamentals - Getting Started with Python

Python Phase 1: Fundamentals - Getting Started with Python

Welcome to Phase 1 of the Python Learning Roadmap! This phase will take you from zero to comfortable with Python's core syntax and fundamental programming constructs. Whether you're coming from another language or starting fresh, by the end of this phase you'll be writing Python code confidently.

What You'll Learn

✅ Python installation and IDE setup
✅ Python syntax basics and data types
✅ Control flow with if/else, match/case, and loops
✅ Functions with parameters (*args, **kwargs)
✅ Lists, tuples, dictionaries, and sets
✅ String manipulation and f-strings
✅ Exception handling basics
✅ Virtual environments with venv

Prerequisites

  • Programming experience: Familiarity with variables, loops, and functions in any language
  • Command line basics: Comfort with terminal/command prompt
  • Time commitment: 2 weeks, 10-15 hours total

Installing Python 3.10+

Download and Install

Python 3.10+ includes modern features like structural pattern matching (match/case) and improved type hints.

macOS/Linux:

# Check if Python is installed
python3 --version
 
# Install via Homebrew (macOS)
brew install python@3.12
 
# Install via apt (Ubuntu/Debian)
sudo apt update
sudo apt install python3.12

Windows:

  1. Download from python.org
  2. Run installer and check "Add Python to PATH"
  3. Verify: python --version

IDE Setup

VS Code (Recommended):

# Install VS Code extensions
code --install-extension ms-python.python
code --install-extension ms-python.vscode-pylance

PyCharm:

Python Syntax Basics

Variables and Data Types

Python uses dynamic typing - no need to declare types explicitly.

# Variables (snake_case naming)
name: str = "Alice"
age: int = 30
height: float = 5.7
is_student: bool = False
 
# Type hints are optional but recommended
user_id: int = 12345
email: str = "alice@example.com"
 
# Multiple assignment
x, y, z = 1, 2, 3

Basic Data Types

# Integers
count: int = 100
negative: int = -50
 
# Floats
price: float = 19.99
scientific: float = 1.5e-4
 
# Strings
message: str = "Hello, Python!"
multiline: str = """
This is a
multiline string
"""
 
# Booleans
is_active: bool = True
has_permission: bool = False
 
# None (null equivalent)
result: str | None = None

Operators

# Arithmetic
x = 10 + 5   # 15
y = 10 - 5   # 5
z = 10 * 5   # 50
w = 10 / 5   # 2.0 (always returns float)
q = 10 // 3  # 3 (floor division)
r = 10 % 3   # 1 (modulo)
p = 2 ** 3   # 8 (exponentiation)
 
# Comparison
10 == 10  # True (equality)
10 != 5   # True (inequality)
10 > 5    # True
10 >= 10  # True
10 < 20   # True
10 <= 10  # True
 
# Logical
True and False  # False
True or False   # True
not True        # False
 
# Identity
x is None       # Check if x is None
x is not None   # Check if x is not None

Control Flow

If/Else Statements

age: int = 18
 
if age >= 18:
    print("Adult")
elif age >= 13:
    print("Teenager")
else:
    print("Child")
 
# Ternary operator
status: str = "Adult" if age >= 18 else "Minor"
 
# Truthiness
name: str = "Alice"
if name:  # Non-empty strings are truthy
    print(f"Hello, {name}")
 
# Check for None
result: str | None = None
if result is None:
    print("No result")

Match/Case (Python 3.10+)

Python's structural pattern matching is more powerful than traditional switch statements.

def handle_status(status_code: int) -> str:
    match status_code:
        case 200:
            return "Success"
        case 404:
            return "Not Found"
        case 500:
            return "Server Error"
        case _:  # Default case
            return "Unknown"
 
# Pattern matching with values
def describe_point(point: tuple[int, int]) -> str:
    match point:
        case (0, 0):
            return "Origin"
        case (0, y):
            return f"On Y-axis at {y}"
        case (x, 0):
            return f"On X-axis at {x}"
        case (x, y):
            return f"Point at ({x}, {y})"

Loops

For loops:

# Iterate over a range
for i in range(5):  # 0, 1, 2, 3, 4
    print(i)
 
# Iterate over a list
fruits: list[str] = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)
 
# Enumerate (index + value)
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")
 
# Iterate over range with step
for i in range(0, 10, 2):  # 0, 2, 4, 6, 8
    print(i)

While loops:

count: int = 0
while count < 5:
    print(count)
    count += 1
 
# Break and continue
for i in range(10):
    if i == 3:
        continue  # Skip 3
    if i == 7:
        break  # Stop at 7
    print(i)

Functions

Basic Functions

def greet(name: str) -> str:
    """Greet a person by name."""
    return f"Hello, {name}!"
 
# Call function
message: str = greet("Alice")
print(message)  # "Hello, Alice!"
 
# Function with default parameter
def greet_with_title(name: str, title: str = "Mr.") -> str:
    return f"Hello, {title} {name}!"
 
print(greet_with_title("Smith"))  # "Hello, Mr. Smith!"
print(greet_with_title("Smith", "Dr."))  # "Hello, Dr. Smith!"

Multiple Return Values

def get_user_info() -> tuple[str, int, str]:
    """Return multiple values as a tuple."""
    return "Alice", 30, "alice@example.com"
 
# Unpack return values
name, age, email = get_user_info()
print(f"{name} is {age} years old")

*args and **kwargs

# *args: Variable positional arguments
def sum_numbers(*args: int) -> int:
    """Sum any number of integers."""
    return sum(args)
 
print(sum_numbers(1, 2, 3))  # 6
print(sum_numbers(1, 2, 3, 4, 5))  # 15
 
# **kwargs: Variable keyword arguments
def print_user_info(**kwargs: str | int) -> None:
    """Print user information."""
    for key, value in kwargs.items():
        print(f"{key}: {value}")
 
print_user_info(name="Alice", age=30, city="NYC")
# name: Alice
# age: 30
# city: NYC
 
# Combined usage
def create_user(name: str, *tags: str, **metadata: str | int) -> dict:
    """Create user with tags and metadata."""
    return {
        "name": name,
        "tags": list(tags),
        "metadata": metadata
    }
 
user = create_user(
    "Alice",
    "developer",
    "python",
    age=30,
    city="NYC"
)

Data Structures

Lists (Mutable, Ordered)

# Create lists
numbers: list[int] = [1, 2, 3, 4, 5]
fruits: list[str] = ["apple", "banana", "cherry"]
mixed: list[int | str] = [1, "two", 3, "four"]
 
# Access elements
first: str = fruits[0]  # "apple"
last: str = fruits[-1]  # "cherry"
 
# Slicing
first_two: list[str] = fruits[0:2]  # ["apple", "banana"]
last_two: list[str] = fruits[-2:]   # ["banana", "cherry"]
 
# Modify lists
fruits.append("orange")  # Add to end
fruits.insert(1, "mango")  # Insert at index
fruits.remove("banana")  # Remove first occurrence
popped: str = fruits.pop()  # Remove and return last item
 
# List methods
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort()  # Sort in place
numbers.reverse()  # Reverse in place
count: int = numbers.count(1)  # Count occurrences
 
# Check membership
if "apple" in fruits:
    print("Found apple!")

Tuples (Immutable, Ordered)

# Create tuples
point: tuple[int, int] = (10, 20)
rgb: tuple[int, int, int] = (255, 128, 0)
 
# Unpack tuples
x, y = point
red, green, blue = rgb
 
# Named tuples (lightweight data classes)
from typing import NamedTuple
 
class Point(NamedTuple):
    x: int
    y: int
 
p = Point(10, 20)
print(p.x, p.y)  # 10 20

Dictionaries (Key-Value Pairs)

# Create dictionaries
user: dict[str, str | int] = {
    "name": "Alice",
    "age": 30,
    "email": "alice@example.com"
}
 
# Access values
name: str = user["name"]  # Raises KeyError if missing
age: int | None = user.get("age")  # Returns None if missing
age_default: int = user.get("age", 0)  # Returns default
 
# Modify dictionaries
user["city"] = "NYC"  # Add/update key
del user["email"]  # Delete key
popped_value = user.pop("age", None)  # Remove and return
 
# Iterate over dictionaries
for key in user:
    print(f"{key}: {user[key]}")
 
for key, value in user.items():
    print(f"{key}: {value}")
 
# Dictionary comprehension
squares: dict[int, int] = {x: x**2 for x in range(5)}
# {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}

Sets (Unique, Unordered)

# Create sets
fruits: set[str] = {"apple", "banana", "cherry"}
numbers: set[int] = {1, 2, 3, 4, 5}
 
# Sets automatically remove duplicates
numbers = {1, 2, 2, 3, 3, 3}  # {1, 2, 3}
 
# Add and remove
fruits.add("orange")
fruits.remove("banana")  # Raises KeyError if missing
fruits.discard("banana")  # No error if missing
 
# Set operations
a: set[int] = {1, 2, 3, 4}
b: set[int] = {3, 4, 5, 6}
 
union: set[int] = a | b  # {1, 2, 3, 4, 5, 6}
intersection: set[int] = a & b  # {3, 4}
difference: set[int] = a - b  # {1, 2}
symmetric_diff: set[int] = a ^ b  # {1, 2, 5, 6}

String Manipulation

String Basics

# Create strings
name: str = "Alice"
message: str = 'Hello, World!'
multiline: str = """
Line 1
Line 2
"""
 
# String concatenation
full_name: str = "Alice" + " " + "Smith"
 
# String repetition
separator: str = "-" * 20  # "--------------------"
 
# String methods
text: str = "  Hello, Python!  "
lower: str = text.lower()  # "  hello, python!  "
upper: str = text.upper()  # "  HELLO, PYTHON!  "
stripped: str = text.strip()  # "Hello, Python!"
replaced: str = text.replace("Python", "World")

F-Strings (Formatted Strings)

name: str = "Alice"
age: int = 30
 
# Basic f-string
message: str = f"My name is {name} and I'm {age} years old"
 
# Expressions in f-strings
result: str = f"Next year I'll be {age + 1}"
 
# Format numbers
price: float = 19.99
formatted: str = f"Price: ${price:.2f}"  # "Price: $19.99"
 
# Align text
item: str = "Apple"
count: int = 5
line: str = f"{item:<10} {count:>5}"  # "Apple          5"
 
# Debug mode (Python 3.8+)
x: int = 10
debug: str = f"{x=}"  # "x=10"

String Operations

# Slicing
text: str = "Python"
first_three: str = text[:3]  # "Pyt"
last_three: str = text[-3:]  # "hon"
 
# Split and join
sentence: str = "Hello, World, Python"
words: list[str] = sentence.split(", ")  # ["Hello", "World", "Python"]
joined: str = " | ".join(words)  # "Hello | World | Python"
 
# Check content
text = "Hello123"
text.isalpha()  # False (contains numbers)
text.isdigit()  # False (contains letters)
text.isalnum()  # True (alphanumeric)
 
# Startswith/Endswith
filename: str = "document.pdf"
if filename.endswith(".pdf"):
    print("It's a PDF!")

File I/O Basics

Reading Files

# Read entire file
with open("data.txt", "r") as file:
    content: str = file.read()
    print(content)
 
# Read line by line
with open("data.txt", "r") as file:
    for line in file:
        print(line.strip())
 
# Read all lines into list
with open("data.txt", "r") as file:
    lines: list[str] = file.readlines()

Writing Files

# Write to file (overwrites)
with open("output.txt", "w") as file:
    file.write("Hello, World!\n")
    file.write("Second line\n")
 
# Append to file
with open("output.txt", "a") as file:
    file.write("Appended line\n")
 
# Write multiple lines
lines: list[str] = ["Line 1\n", "Line 2\n", "Line 3\n"]
with open("output.txt", "w") as file:
    file.writelines(lines)

Working with Paths

from pathlib import Path
 
# Create Path object
file_path = Path("data/input.txt")
 
# Check existence
if file_path.exists():
    print("File exists!")
 
# Read with Path
content: str = file_path.read_text()
 
# Write with Path
file_path.write_text("Hello, World!")
 
# Get file info
print(file_path.name)  # "input.txt"
print(file_path.stem)  # "input"
print(file_path.suffix)  # ".txt"
print(file_path.parent)  # Path("data")

Exception Handling

Try/Except Basics

# Basic exception handling
try:
    number: int = int("abc")  # ValueError
except ValueError:
    print("Invalid number!")
 
# Handle multiple exceptions
try:
    result: int = 10 / 0
except ZeroDivisionError:
    print("Cannot divide by zero!")
except ValueError:
    print("Invalid value!")
 
# Catch all exceptions (use sparingly)
try:
    # risky operation
    pass
except Exception as e:
    print(f"Error occurred: {e}")

Finally and Else

# Finally: Always executes
try:
    file = open("data.txt", "r")
    content = file.read()
except FileNotFoundError:
    print("File not found!")
finally:
    if 'file' in locals():
        file.close()
 
# Else: Executes if no exception
try:
    result: int = 10 / 2
except ZeroDivisionError:
    print("Error!")
else:
    print(f"Result: {result}")  # Executes
finally:
    print("Cleanup")  # Always executes

Raising Exceptions

def divide(a: int, b: int) -> float:
    """Divide two numbers."""
    if b == 0:
        raise ValueError("Cannot divide by zero!")
    return a / b
 
# Custom exception
class InvalidAgeError(Exception):
    """Raised when age is invalid."""
    pass
 
def set_age(age: int) -> None:
    if age < 0:
        raise InvalidAgeError("Age cannot be negative!")
    print(f"Age set to {age}")

Virtual Environments

Why Virtual Environments?

Virtual environments isolate project dependencies to avoid conflicts between projects.

Creating and Using venv

# Create virtual environment
python3 -m venv venv
 
# Activate (macOS/Linux)
source venv/bin/activate
 
# Activate (Windows)
venv\Scripts\activate
 
# Install packages
pip install requests
 
# List installed packages
pip list
 
# Save dependencies
pip freeze > requirements.txt
 
# Install from requirements
pip install -r requirements.txt
 
# Deactivate
deactivate

Project Structure

my-project/
├── venv/              # Virtual environment (don't commit)
├── src/               # Source code
│   └── main.py
├── tests/             # Test files
├── requirements.txt   # Dependencies
└── .gitignore        # Ignore venv/

Best Practices

Code Style

Use snake_case for variables and functions
Use PascalCase for classes
Use UPPERCASE for constants
Add type hints for function parameters and return types
Write docstrings for functions

# Good
def calculate_total_price(items: list[float]) -> float:
    """Calculate the total price of items."""
    return sum(items)
 
# Bad
def CalculatePrice(x):  # Wrong naming, no types
    return sum(x)

Common Pitfalls

❌ Mutable default arguments:

# BAD - list is shared across calls!
def add_item(item: str, items: list[str] = []) -> list[str]:
    items.append(item)
    return items
 
# GOOD
def add_item(item: str, items: list[str] | None = None) -> list[str]:
    if items is None:
        items = []
    items.append(item)
    return items

❌ Modifying list while iterating:

# BAD
numbers = [1, 2, 3, 4, 5]
for num in numbers:
    if num % 2 == 0:
        numbers.remove(num)  # Causes skipping!
 
# GOOD
numbers = [num for num in numbers if num % 2 != 0]

Practice Exercises

Exercise 1: Temperature Converter

def celsius_to_fahrenheit(celsius: float) -> float:
    """Convert Celsius to Fahrenheit."""
    return (celsius * 9/5) + 32
 
def fahrenheit_to_celsius(fahrenheit: float) -> float:
    """Convert Fahrenheit to Celsius."""
    return (fahrenheit - 32) * 5/9

Exercise 2: Word Counter

def count_words(text: str) -> dict[str, int]:
    """Count word frequencies in text."""
    words = text.lower().split()
    word_counts: dict[str, int] = {}
 
    for word in words:
        word_counts[word] = word_counts.get(word, 0) + 1
 
    return word_counts
 
# Example
text = "hello world hello python world"
print(count_words(text))
# {'hello': 2, 'world': 2, 'python': 1}

Exercise 3: Simple TODO List

def todo_app() -> None:
    """Simple TODO list application."""
    todos: list[str] = []
 
    while True:
        print("\n1. Add task")
        print("2. View tasks")
        print("3. Remove task")
        print("4. Quit")
 
        choice: str = input("Choose option: ")
 
        match choice:
            case "1":
                task = input("Enter task: ")
                todos.append(task)
            case "2":
                for i, task in enumerate(todos, 1):
                    print(f"{i}. {task}")
            case "3":
                index = int(input("Task number: ")) - 1
                if 0 <= index < len(todos):
                    todos.pop(index)
            case "4":
                break

Summary and Key Takeaways

✅ Python uses dynamic typing but type hints improve code quality
✅ Use f-strings for string formatting (modern and readable)
✅ Match/case is powerful for pattern matching (Python 3.10+)
✅ Lists are mutable, tuples are immutable
✅ Dictionaries map keys to values (fast lookups)
✅ Sets store unique elements (great for membership testing)
✅ Always use with statements for file operations
✅ Handle exceptions gracefully with try/except
✅ Use virtual environments for every project
✅ Follow PEP 8 style guide for readable code

Next Steps

You've completed Phase 1! You now understand Python's core syntax and can write basic programs. Ready to level up?

Continue to: Phase 2: OOP & Advanced Features →

Back to roadmap: Python Learning Roadmap →

Recommended practice: Build a CLI tool (calculator, file organizer, or password generator) to reinforce these fundamentals before moving on!

📬 Subscribe to Newsletter

Get the latest blog posts delivered to your inbox every week. No spam, unsubscribe anytime.

We respect your privacy. Unsubscribe at any time.

💬 Comments

Sign in to leave a comment

We'll never post without your permission.