Skip to content

Getting Back to Python

This tutorial assumes you already learned Python once. You’re not starting from zero — you’re rebuilding fluency.

Variables and Data Types: How Python Stores Information

Python is dynamically typed: variables point to objects, not fixed types.

x = 10
x = "hello"   # same variable, new object

Core built-in data types

Type Purpose Mutable
int Whole numbers
float Decimal numbers
bool True / False
str Text
list Ordered collection
tuple Fixed ordered collection
dict Key–value mapping
set Unique unordered values
NoneType Represents “nothing”

Strings: Text Is a Sequence

s = "python"

Accessing characters

s[0]     # 'p'
s[-1]    # 'n'
s[1:4]   # 'yth'

Common operations

s.upper()
s.lower()
s.replace("py", "Py")
" ".join(["hello", "world"])

When to use strings

  • Any human-readable text
  • File contents
  • Identifiers, labels, names

Strings are immutable — every modification creates a new string.

Lists: Ordered, Changeable Collections

nums = [10, 20, 30]

Accessing elements

nums[0]        # 10
nums[-1]       # 30
nums[1:3]      # [20, 30]

Modifying lists

nums.append(40)
nums[1] = 25
nums.remove(10)

Looping

for n in nums:
    print(n)

When to use lists

  • Ordered data
  • When duplicates matter
  • When you need to add/remove items

Tuples: Fixed, Ordered Data

point = (10, 20)

Accessing

point[0]
point[1]

Tuples cannot be changed:

point[0] = 5  # error

When to use tuples

  • Fixed-size data (coordinates, RGB values)
  • When structure matters more than mutability
  • As dictionary keys

Dictionaries: Key–Value Data

user = {
    "name": "Ada",
    "age": 32,
    "admin": True
}

Accessing values

user["name"]
user.get("email")        # safer, returns None

Modifying

user["age"] = 33
user["email"] = "ada@example.com"

Looping

for key, value in user.items():
    print(key, value)

When to use dictionaries

  • Named data
  • Structured records
  • Fast lookups by key

Think of dicts as Python’s basic data structure for real-world objects.

Sets: Unique, Unordered Values

tags = {"python", "coding", "python"}

Result:

{"python", "coding"}

Operations

tags.add("tutorial")
"a" in tags

Set math

a = {1, 2, 3}
b = {3, 4}

a & b    # intersection → {3}
a | b    # union → {1, 2, 3, 4}
a - b    # difference → {1, 2}

When to use sets

  • Remove duplicates
  • Membership tests
  • Comparing collections

Conditions: Making Decisions

x = 10

if x > 5:
    print("big")
elif x == 5:
    print("exact")
else:
    print("small")

Truthiness

if []:
    print("won't run")

if "text":
    print("runs")

Falsy values:

  • 0
  • ""
  • [], {}, set()
  • None
  • False

Loops: Repeating Work

for loops

for i in range(3):
    print(i)
for key in user:
    print(key)
for i, value in enumerate(nums):
    print(i, value)

while loops

count = 0
while count < 3:
    count += 1

Use for when possible; while when the end condition is unknown.

Functions: Reusable Behavior

def add(a, b):
    return a + b

Parameters and return values

def greet(name, excited=False):
    msg = f"Hello {name}"
    if excited:
        msg += "!"
    return msg

Functions that modify data

def normalize(nums):
    total = sum(nums)
    return [n / total for n in nums]

When to write a function

  • You repeat code
  • You want clarity
  • You want to test logic in isolation

Accessing Data Inside Data Structures

Nested structures

data = {
    "users": [
        {"name": "Ada", "age": 32},
        {"name": "Linus", "age": 54}
    ]
}

Access:

data["users"][0]["name"]   # "Ada"

Defensive access

user.get("profile", {}).get("email")

File Handling: Reading and Writing Data

Reading a text file

with open("input.txt", "r") as f:
    content = f.read()

Line-by-line

with open("input.txt") as f:
    for line in f:
        print(line.strip())

Writing a file

with open("output.txt", "w") as f:
    f.write("Hello\n")

Appending

with open("log.txt", "a") as f:
    f.write("New entry\n")

The with statement:

  • Automatically closes files
  • Prevents resource leaks
  • Is the correct default

Common Data File Types

JSON (structured data)

import json

with open("data.json") as f:
    data = json.load(f)

with open("out.json", "w") as f:
    json.dump(data, f, indent=2)

JSON maps naturally to:

  • dict → object
  • list → array

CSV (tabular data)

import csv

with open("data.csv") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

Choosing the Right Data Type

Need Use
Ordered data list
Fixed structure tuple
Named fields dict
Uniqueness set
Text str
Absence None

Rule of thumb:

If you’re naming things → use a dict. If you’re counting things → use a list or set.

Python Functions — The Essentials

What is a function?

A function is a named block of code that:

  • takes input
  • does something
  • returns output
def add(a, b):
    return a + b

Use it:

result = add(2, 3)

Parameters and return values

def square(x):
    return x * x

No return → returns None.

def log(msg):
    print(msg)

Default and keyword arguments

def greet(name, excited=False):
    return f"Hello {name}!" if excited else f"Hello {name}"
greet("Ada")
greet("Ada", excited=True)

Functions and data structures

Functions usually take and return lists or dicts.

def normalize(values):
    total = sum(values)
    return [v / total for v in values]

Good practice:

  • Don’t modify inputs unless you mean to
  • Return new data

Returning multiple values

Python returns a tuple.

def min_max(nums):
    return min(nums), max(nums)

low, high = min_max([1, 5, 9])

Local variables and scope

x = 10

def foo():
    x = 5
    return x

Inside ≠ outside. Avoid using global.

Functions calling functions

Small functions compose well.

def square(x):
    return x * x

def sum_of_squares(nums):
    return sum(square(n) for n in nums)

Basic error handling inside functions

def parse_int(text):
    try:
        return int(text)
    except ValueError:
        return None

Classes

What is a class

A class groups data (attributes) and behavior (methods) together.

Think of a class as:

A blueprint for creating objects.

class Counter:
    pass

An object (instance) is created like this:

c = Counter()

Why classes exist

Use classes when:

  • Data and functions naturally belong together
  • You’re passing around the same group of values
  • You want to model a real “thing”

If functions are verbs, classes are nouns.

The __init__ method

__init__ runs when an object is created.

class Counter:
    def __init__(self):
        self.value = 0
  • self refers to the current object
  • Attributes are attached to self
c = Counter()
c.value    # 0

Instance methods

Methods are functions that belong to a class.

class Counter:
    def __init__(self):
        self.value = 0

    def increment(self):
        self.value += 1

Usage:

c = Counter()
c.increment()
c.value

Attributes vs local variables

class Example:
    def set(self):
        x = 10          # local variable
        self.y = 20     # attribute
  • x exists only inside the method
  • self.y exists on the object

Passing data into a class

class User:
    def __init__(self, name, role):
        self.name = name
        self.role = role
u = User("Ada", "admin")
u.name
u.role

Methods that return values

class Rectangle:
    def __init__(self, w, h):
        self.w = w
        self.h = h

    def area(self):
        return self.w * self.h
r = Rectangle(3, 4)
r.area()

Classes and data structures

Objects often wrap dictionaries or lists.

class Cart:
    def __init__(self):
        self.items = []

    def add(self, name, price):
        self.items.append({"name": name, "price": price})

    def total(self):
        return sum(item["price"] for item in self.items)

When NOT to use classes

Don’t use a class if:

  • A function and dict are enough
  • There’s no shared state
  • You only need one operation

Many Python programs are mostly functions + dicts.


A common beginner mistake

❌ Doing too much in one class

class App:
    def load(self): ...
    def process(self): ...
    def email(self): ...

✅ Prefer small, focused classes

class Loader: ...
class Processor: ...

__str__: Make objects readable

class User:
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return f"User({self.name})"
print(User("Ada"))

Very light inheritance (optional)

Use inheritance only when there is a true “is-a” relationship.

class Animal:
    def speak(self):
        return "sound"

class Dog(Animal):
    def speak(self):
        return "woof"
Dog().speak()

If this feels unnecessary — skip it. Many Python codebases barely use inheritance.


Classes vs functions — quick rule

Use case Prefer
One operation Function
Multiple operations on shared data Class
Data with behavior Class
Stateless logic Function

The shape of real class-based code

class FileReader:
    def __init__(self, path):
        self.path = path

    def read_lines(self):
        with open(self.path) as f:
            return f.readlines()
reader = FileReader("data.txt")
lines = reader.read_lines()