# Python References

Course: [Python](https://www.devtutorials.ai/learn/python) · Section: References
Source: https://www.devtutorials.ai/learn/python/references/python-references

> A lookup page for the whole course: one table per topic, from data types and operators to files, class relationships and threads.

You have reached the end of the course. Twenty-six pages, and nobody remembers all of it.

Nor should they. What an experienced programmer actually keeps in their head is a map: *there is a method that does this, and it is on strings.* The exact spelling is looked up — every day, by everybody.

This page is that lookup. One table per topic, in the order you met them, with a runnable cell wherever seeing the answer beats reading about it.

Here is the whole course in one small program — read it and you will meet most of what you have learnt, working together:

```python runnable title="The whole course in one program"
from dataclasses import dataclass

@dataclass
class Result:
    name: str
    subject: str
    mark: int

    @property
    def grade(self):
        return "pass" if self.mark >= 35 else "fail"

results = [
    Result("Asha", "Maths", 78),
    Result("Ravi", "Science", 85),
    Result("Meera", "English", 30),
]

for result in sorted(results, key=lambda r: r.mark, reverse=True):
    print(f"{result.name:<8}{result.subject:<10}{result.mark:>4}  {result.grade}")

passed = [r for r in results if r.grade == "pass"]
print(f"\n{len(passed)} of {len(results)} passed, average {sum(r.mark for r in results) / len(results):.1f}")
```

Use this page the way you would use a dictionary: not read front to back, but opened at the row you need. Every page title below is a lesson you can go back to.

## What You Will Learn

This page is a reference rather than a lesson. It gives you:

- One table per topic, in course order, with the syntax you will look up most
- A runnable cell for the things that are easier seen than described
- The traps worth remembering, collected from every page in one place

By the end of this page, you will be able to find any piece of Python syntax from the whole course in one place, and know the handful of traps that are worth keeping in your head rather than looking up.

## Data Types

Here is every type you met, side by side:

| Type | Example | Mutable | Notes |
|---|---|---|---|
| `int` | `78` | — | No size limit |
| `float` | `78.5` | — | Not exact: `0.1 + 0.2 != 0.3` |
| `complex` | `3 + 4j` | — | `j`, not `i` |
| `bool` | `True` | — | A kind of `int`: `True == 1` |
| `str` | `"Asha"` | No | Every method returns a new string |
| `list` | `[78, 85]` | Yes | Ordered, duplicates allowed |
| `tuple` | `(78, 85)` | No | The comma makes it, not the brackets |
| `set` | `{78, 85}` | Yes | No duplicates, no order |
| `dict` | `{"Asha": 78}` | Yes | Keys must be hashable |
| `NoneType` | `None` | — | Compare with `is`, never `==` |

```python runnable title="Checking and converting"
value = "78"

print(type(value), isinstance(value, str))
print(int(value), float(value), bool(value))
print(int(78.9), str(78), list("Asha"))
print(bool(""), bool(0), bool([]), bool("0"))
```

That last line is the one to remember: empty things are false, and `"0"` is a non-empty string, so it is true.

## Keywords

Keywords are reserved words that Python uses as part of the language itself and cannot normally be used as variable names.

```python runnable title="Every keyword in the language"
import keyword

print(len(keyword.kwlist))
print(keyword.kwlist)
print(keyword.softkwlist)
print(keyword.iskeyword("class"), keyword.iskeyword("classroom"))
```

Soft keywords — `match`, `case`, `_`, `type` — are only keywords in their own context, so a variable called `match` is still allowed.

## Operators

Here are the groups, and then the ones that behave unexpectedly:

| Group | Operators |
|---|---|
| Arithmetic | `+` `-` `*` `/` `//` `%` `**` |
| Comparison | `==` `!=` `>` `<` `>=` `<=` |
| Assignment | `=` `+=` `-=` `*=` `/=` `//=` `%=` `**=` |
| Logical | `and` `or` `not` |
| Identity | `is` `is not` |
| Membership | `in` `not in` |
| Bitwise | `&` `\|` `^` `~` `<<` `>>` |
| Walrus | `:=` |

```python runnable title="The ones that surprise people"
print(7 / 2, 7 // 2, 7 % 2, 2 ** 10)
print(-7 // 2)
print(0.1 + 0.2, 0.1 + 0.2 == 0.3)
print(round(0.1 + 0.2, 2) == 0.3)
print("a" in "Asha", 78 in [78, 85])
print(True + True)
```

`-7 // 2` is `-4`, not `-3`: floor division rounds **down**, not towards zero.

## Conditional Statements

```text
if condition:
    ...
elif other_condition:
    ...
else:
    ...

value = a if condition else b

match command:
    case "add":
        ...
    case "remove" | "delete":
        ...
    case _:
        ...
```

In a `match`, `case _` must come **last** and a bare name as a case matches everything. Both of the tempting mistakes here — putting `case _` first, or a bare name in the middle — are hard `SyntaxError`s, which is the one mercy of this feature.

## Loops

Here are the forms, and what each is for:

| Form | Use |
|---|---|
| `for item in things:` | Walk through a collection |
| `for i in range(5):` | Repeat a fixed number of times |
| `for i, item in enumerate(things):` | Position and item together |
| `for a, b in zip(xs, ys):` | Two collections side by side |
| `while condition:` | Repeat until something changes |
| `break` | Leave the loop now |
| `continue` | Skip to the next turn |
| `else:` on a loop | Runs only if no `break` happened |

```python runnable title="range, enumerate and zip"
print(list(range(5)), list(range(2, 10, 3)), list(range(5, 0, -1)))

names = ["Asha", "Ravi"]
marks = [78, 85]

for position, name in enumerate(names, start=1):
    print(position, name)

for name, mark in zip(names, marks):
    print(name, mark)

for mark in marks:
    if mark > 90:
        break
else:
    print("No mark above 90 — the else ran")
```

## Functions

```text
def name(positional, default=1, *args, keyword_only, **kwargs) -> int:
    """What it does."""
    return value

lambda x: x * 2
```

Here is what the pieces mean:

| Thing | Meaning |
|---|---|
| `*args` | The extra positional arguments, as a tuple |
| `**kwargs` | The extra keyword arguments, as a dictionary |
| `global x` | Assign to a module-level name |
| `nonlocal x` | Assign to the enclosing function's name |
| LEGB | Local, Enclosing, Global, Built-in — the search order |

Never write `def add(item, basket=[])`. The default list is created once, when the function is defined, and every call that leaves it out shares that same list. Use `basket=None` and build it inside.

```python runnable title="The mutable default, shown"
def bad(item, basket=[]):
    basket.append(item)
    return basket

def good(item, basket=None):
    basket = [] if basket is None else basket
    basket.append(item)
    return basket

print(bad("Asha"), bad("Ravi"))
print(good("Asha"), good("Ravi"))
```

## Modules and Packages

Modules and packages are how Python code is organised and reused across files.

```text
import math
import math as m
from math import sqrt
from math import sqrt as root
from math import *            # avoid

if __name__ == "__main__":
    main()
```

A **module** is a `.py` file. A **package** is a folder of modules, usually with an `__init__.py`. `__name__` is `"__main__"` when the file is run directly, and the module's own name when it is imported — which is what the guard above tests.

## Array

```python runnable title="The array module, and why a list is usually better"
from array import array

marks = array("i", [78, 85, 60])

print(marks, marks.typecode, marks.itemsize)
marks.append(72)
marks.insert(0, 90)
print(marks.tolist(), marks.index(85), sum(marks))

try:
    marks.append("ninety")
except TypeError as error:
    print("TypeError:", error)
```

An `array` holds one type only, which is its point and its limitation. For everyday work, use a list.

## String Methods

Here is the full toolkit, grouped by job:

| Group | Methods |
|---|---|
| Case | `upper` `lower` `title` `capitalize` `swapcase` `casefold` |
| Search | `find` `rfind` `index` `count` `startswith` `endswith` |
| Check | `isdigit` `isalpha` `isalnum` `isspace` `isupper` `islower` `istitle` |
| Change | `replace` `strip` `lstrip` `rstrip` `removeprefix` `removesuffix` |
| Split and join | `split` `rsplit` `splitlines` `partition` `join` |
| Pad | `ljust` `rjust` `center` `zfill` |

```python runnable title="The ones used daily"
line = "  Asha,Maths,78  "

print(repr(line.strip()))
print(line.strip().split(","))
print("-".join(["Asha", "Maths", "78"]))
print("Asha".ljust(10, ".") + "78".rjust(5))
print("asha".title(), "ASHA".lower())
print("78".isdigit(), "78.5".isdigit())
```

Remember that strings are immutable: every one of those returns a **new** string and leaves the original alone.

## String Formatting

```python runnable title="Four ways, and the one to use"
name, mark = "Asha", 78.456

print("%s scored %.1f" % (name, mark))
print("{} scored {:.1f}".format(name, mark))
print(f"{name} scored {mark:.1f}")
print(f"{name=}, {mark=}")
print(f"{name:<10}|{mark:>8.2f}|{mark:^10.0f}|")
print(f"{1234567:,} and {0.256:.1%}")
```

Here is what each spec means:

| Spec | Meaning |
|---|---|
| `:<10` `:>10` `:^10` | Left, right, centre in 10 spaces |
| `:.2f` | Two decimal places |
| `:,` | Thousands separator |
| `:.1%` | As a percentage |
| `{name=}` | Prints the expression and its value |

## Date and Time

```python runnable title="Fixed dates, so the answer is the same every time"
from datetime import date, time, datetime, timedelta

exam = date(2026, 3, 15)
print(exam, exam.year, exam.strftime("%d %B %Y"), exam.weekday())

bell = time(9, 30)
print(bell, bell.hour)

start = datetime(2026, 3, 15, 9, 30)
print(start.strftime("%d/%m/%Y %H:%M"))
print(datetime.strptime("15/03/2026", "%d/%m/%Y").date())

print(exam + timedelta(days=30))
print((date(2026, 4, 14) - exam).days)
```

Here is the code for each part of a date:

| Code | Means |
|---|---|
| `%d` `%m` `%Y` | Day, month, four-digit year |
| `%B` `%b` | Month name, short month name |
| `%H` `%M` `%S` | Hour (24), minute, second |
| `%A` `%a` | Weekday name, short weekday |

`date.today()` and `datetime.now()` give a different answer every run — useful in a program, awkward in an example.

## Classes and Objects

```text
class Student:
    school = "Greenfield"              # class attribute, shared

    def __init__(self, name, mark):
        self.name = name              # instance attribute
        self._mark = mark             # by convention, internal
        self.__secret = "hidden"      # name-mangled

    def describe(self):               # instance method
        return f"{self.name}: {self._mark}"

    def __str__(self):
        return self.describe()
```

Here is which magic method answers which operation:

| Magic method | Called by |
|---|---|
| `__init__` | Creating the object |
| `__str__` | `print()` and `str()` |
| `__repr__` | The shell, and `repr()` |
| `__len__` | `len()` |
| `__eq__` | `==` |
| `__lt__` | `<` and `sorted()` |
| `__add__` | `+` |
| `__getitem__` | `thing[0]` |
| `__contains__` | `in` |
| `__call__` | `thing()` |

## Class and Static Methods

Python supports three kinds of methods, each used for a different purpose.

```python runnable title="Three kinds of method"
class Student:
    school = "Greenfield"

    def __init__(self, name, mark):
        self.name = name
        self.mark = mark

    def describe(self):
        return f"{self.name}: {self.mark}"

    @classmethod
    def from_text(cls, text):
        name, mark = text.split(",")
        return cls(name, int(mark))

    @staticmethod
    def passed(mark):
        return mark >= 35

asha = Student.from_text("Asha,78")
print(asha.describe())
print(Student.passed(20), Student.passed(78))
print(Student.school, asha.school)
```

- An **instance method** takes `self` — it works on one object.
- A **class method** takes `cls` — it usually builds one, which is why `from_text` exists.
- A **static method** takes neither — it is a plain function kept with the class because it belongs there.

## Inheritance, Polymorphism, Abstraction and Encapsulation

```python runnable title="All four in one example"
from abc import ABC, abstractmethod

class Person(ABC):
    def __init__(self, name):
        self._name = name

    @property
    def name(self):
        return self._name

    @abstractmethod
    def role(self):
        ...

    def describe(self):
        return f"{self.name} is a {self.role()}"

class Student(Person):
    def role(self):
        return "student"

class Teacher(Person):
    def __init__(self, name, subject):
        super().__init__(name)
        self.subject = subject

    def role(self):
        return f"{self.subject} teacher"

for person in [Student("Asha"), Teacher("Mrs Rao", "Maths")]:
    print(person.describe())

try:
    Person("Nobody")
except TypeError as error:
    print("TypeError:", error)
```

Here are the four ideas, side by side:

| Idea | What it means | How it is written |
|---|---|---|
| Inheritance | A child is a kind of its parent | `class Student(Person)` |
| Polymorphism | One call, the right version runs | Overriding `role()` |
| Abstraction | What, not how; the base refuses to be built | `ABC` and `@abstractmethod` |
| Encapsulation | The inside is reached through a door you control | `_name` and `@property` |

Both `ABC` and `@abstractmethod` are needed. With only one of them, the class can still be created and nothing complains.

## Collections

Here are the four, side by side:

| | Written | Ordered | Changeable | Duplicates |
|---|---|---|---|---|
| List | `[1, 2]` | Yes | Yes | Yes |
| Tuple | `(1, 2)` | Yes | No | Yes |
| Set | `{1, 2}` | No | Yes | No |
| Dictionary | `{"a": 1}` | Yes | Yes | Keys, no |

```python runnable title="One job done four ways"
marks = [78, 85, 60, 78]

print(sorted(marks), len(marks), sum(marks), max(marks))
print(tuple(marks), set(marks))
print({name: mark for name, mark in zip(["Asha", "Ravi"], [78, 85])})

by_name = {"Asha": 78, "Ravi": 85}
print(by_name.get("Meera", 0), list(by_name.items()))
print({"Asha", "Ravi"} & {"Ravi", "Meera"}, {"Asha", "Ravi"} | {"Meera"})
```

## Exception Handling

```text
try:
    risky()
except ValueError as error:
    ...
except (TypeError, KeyError):
    ...
else:
    ...            # only if nothing was raised
finally:
    ...            # always

raise ValueError("message")
raise NewError("message") from error

class SchoolError(Exception):
    pass
```

Here is which error you get for which mistake:

| Exception | Raised when |
|---|---|
| `NameError` | A name was never defined |
| `TypeError` | Wrong kind of thing |
| `ValueError` | Right kind, impossible value |
| `ZeroDivisionError` | Divided by zero |
| `IndexError` | Position past the end |
| `KeyError` | Key not in the dictionary |
| `AttributeError` | No such method or attribute |
| `ImportError` | That name is not in the module |
| `ModuleNotFoundError` | No such module |
| `FileNotFoundError` | No such file |

The **first matching `except` wins**, so a general type above a specific one makes the specific block unreachable — and Python will not warn you. Most specific first, most general last.

## File Handling

Here is what each mode does — the two `w` rows are the ones to read twice:

| Mode | Opens for | If it does not exist | If it does |
|---|---|---|---|
| `r` | Reading | `FileNotFoundError` | Reads it |
| `w` | Writing | Creates it | **Empties it** |
| `a` | Appending | Creates it | Adds at the end |
| `x` | Creating | Creates it | `FileExistsError` |
| `r+` | Read and write | `FileNotFoundError` | Keeps it |
| `w+` | Write and read | Creates it | **Empties it** |
| `a+` | Append and read | Creates it | Adds at the end |
| `b` | Added to any: bytes, not text | | |

```python runnable title="Write, read, append"
with open("ref_marks.txt", "w") as sheet:
    sheet.write("Asha,78\n")

with open("ref_marks.txt", "a") as sheet:
    sheet.write("Ravi,85\n")

with open("ref_marks.txt") as sheet:
    print(sheet.read())

with open("ref_marks.txt") as sheet:
    print(sheet.readlines())
```

Always use `with`. It closes the file even if something raises — and an unclosed file can still be empty on disk.

## Aggregation and Composition

Here are the two has-a relationships, side by side:

| | Aggregation | Composition |
|---|---|---|
| The part is | Made outside, passed in | Built inside |
| Can be shared | Yes | No |
| When the whole goes | The part stays | The part goes |
| Example | A classroom and its teacher | A classroom and its desks |

```text
class Classroom:
    def __init__(self, label, teacher, desk_count):
        self.teacher = teacher                       # aggregation
        self.desks = [Desk(n) for n in range(desk_count)]   # composition
```

## Multithreading

These are the threading pieces worth remembering after the lesson.

```text
import threading

worker = threading.Thread(target=fetch, args=("Asha",), daemon=False)
worker.start()
worker.join()

lock = threading.Lock()
with lock:
    total += 1
```

Here are the pieces and their jobs:

| Piece | For |
|---|---|
| `Thread` | One thread |
| `start()` / `join()` | Begin it / wait for it |
| `Lock` | One thread at a time |
| `Event` | A flag one thread sets, another waits for |
| `current_thread()` | The thread running this line |
| `ThreadPoolExecutor` | What you should actually use |

Threads help with **waiting**, not with arithmetic — Python's global interpreter lock sees to that. And the browser running this page cannot start one at all, which is why that page is mostly code to read.

When you cannot remember a method, ask Python rather than searching. `dir("")` lists everything a string can do, and `help(str.strip)` explains one of them. Those two work in any Python anywhere, including this page.

```python runnable title="Asking Python instead of searching"
print([name for name in dir(str) if name.startswith("r")])
print(len([name for name in dir(list) if not name.startswith("_")]))
print([name for name in dir(dict) if not name.startswith("_")])
```

## Common mistakes

Before moving on, here are some of the mistakes from the whole course, collected in one place.

Each row is one mistake. The middle column is what you will actually see on screen.

<div className="tscroll">

| Mistake | What you see | Why it happens | Fix |
|---|---|---|---|
| `0.1 + 0.2 == 0.3` | `False` | Floats are binary approximations | `round(..., 2)`, or `math.isclose` |
| `-7 // 2` expected to be `-3` | `-4` | Floor division rounds down | `int(-7 / 2)` to round towards zero |
| `(78)` meant as a tuple | An `int` | The comma makes a tuple | `(78,)` |
| `basket=[]` as a default | Every call shares one list | Defaults are made once | `basket=None` |
| `if name == None` | Works, but wrongly for odd types | `None` is one object | `if name is None` |
| Changing a list while looping it | Items skipped | The positions shift | Loop over a copy |
| `except Exception` above `except ValueError` | The specific block never runs | First match wins | Specific first |
| Opening with `w` to add | The file is emptied | `w` truncates | Use `a` |
| `from module import *` | Names appear from nowhere | Everything was imported | Import what you need |
| A lambda given a name | Tracebacks say `<lambda>` | It was created without a name | Use `def` |
| `Thread(target=fetch())` | Runs immediately, on this thread | The brackets called it | `target=fetch` |
| `self` left out of a method | `TypeError` about arguments | The object is passed first | `def method(self):` |

</div>

## What to Remember

- Nobody remembers all of this. Knowing that a thing exists is the skill; the spelling is a lookup.
- `dir(thing)` lists what an object can do, and `help(thing.method)` explains one. No internet needed.
- Empty things are false: `""`, `0`, `[]`, `{}`, `None`. `"0"` is not empty, so it is true.
- Floats are approximations. Compare them with `round()` or `math.isclose`, never with `==`.
- Strings and tuples cannot be changed; lists, sets and dictionaries can.
- A list method changes in place and returns `None`; the built-in function returns a new object.
- `is` compares identity, `==` compares value. `None` always takes `is`.
- A mutable default argument is created once and shared for ever.
- The first matching `except` wins, so go specific to general.
- Opening a file with `w` empties it. Always use `with`.
- Inheritance is is-a; aggregation and composition are both has-a.
- Threads help with waiting, not with arithmetic.
- When you are stuck, the error message is usually telling you the answer. Read the last line first.

Next: **You have finished the course**
