Python References

By the Syncfusion DevTutorials team · Last updated

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:

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:

TypeExampleMutableNotes
int78—No size limit
float78.5—Not exact: 0.1 + 0.2 != 0.3
complex3 + 4j—j, not i
boolTrue—A kind of int: True == 1
str"Asha"NoEvery method returns a new string
list[78, 85]YesOrdered, duplicates allowed
tuple(78, 85)NoThe comma makes it, not the brackets
set{78, 85}YesNo duplicates, no order
dict{"Asha": 78}YesKeys must be hashable
NoneTypeNone—Compare with is, never ==

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.

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:

GroupOperators
Arithmetic+ - * / // % **
Comparison== != > < >= <=
Assignment= += -= *= /= //= %= **=
Logicaland or not
Identityis is not
Membershipin not in
Bitwise& | ^ ~ << >>
Walrus:=

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

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 SyntaxErrors, which is the one mercy of this feature.

Loops

Here are the forms, and what each is for:

FormUse
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
breakLeave the loop now
continueSkip to the next turn
else: on a loopRuns only if no break happened

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

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:

ThingMeaning
*argsThe extra positional arguments, as a tuple
**kwargsThe extra keyword arguments, as a dictionary
global xAssign to a module-level name
nonlocal xAssign to the enclosing function's name
LEGBLocal, 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.

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.

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

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:

GroupMethods
Caseupper lower title capitalize swapcase casefold
Searchfind rfind index count startswith endswith
Checkisdigit isalpha isalnum isspace isupper islower istitle
Changereplace strip lstrip rstrip removeprefix removesuffix
Split and joinsplit rsplit splitlines partition join
Padljust rjust center zfill

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

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:

SpecMeaning
:<10 :>10 :^10Left, right, centre in 10 spaces
:.2fTwo decimal places
:,Thousands separator
:.1%As a percentage
{name=}Prints the expression and its value

Date and Time

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:

CodeMeans
%d %m %YDay, month, four-digit year
%B %bMonth name, short month name
%H %M %SHour (24), minute, second
%A %aWeekday 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

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 methodCalled 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.

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

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:

IdeaWhat it meansHow it is written
InheritanceA child is a kind of its parentclass Student(Person)
PolymorphismOne call, the right version runsOverriding role()
AbstractionWhat, not how; the base refuses to be builtABC and @abstractmethod
EncapsulationThe 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:

WrittenOrderedChangeableDuplicates
List[1, 2]YesYesYes
Tuple(1, 2)YesNoYes
Set{1, 2}NoYesNo
Dictionary{"a": 1}YesYesKeys, no

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

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:

ExceptionRaised when
NameErrorA name was never defined
TypeErrorWrong kind of thing
ValueErrorRight kind, impossible value
ZeroDivisionErrorDivided by zero
IndexErrorPosition past the end
KeyErrorKey not in the dictionary
AttributeErrorNo such method or attribute
ImportErrorThat name is not in the module
ModuleNotFoundErrorNo such module
FileNotFoundErrorNo 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:

ModeOpens forIf it does not existIf it does
rReadingFileNotFoundErrorReads it
wWritingCreates itEmpties it
aAppendingCreates itAdds at the end
xCreatingCreates itFileExistsError
r+Read and writeFileNotFoundErrorKeeps it
w+Write and readCreates itEmpties it
a+Append and readCreates itAdds at the end
bAdded to any: bytes, not text

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:

AggregationComposition
The part isMade outside, passed inBuilt inside
Can be sharedYesNo
When the whole goesThe part staysThe part goes
ExampleA classroom and its teacherA classroom and its desks
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.

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:

PieceFor
ThreadOne thread
start() / join()Begin it / wait for it
LockOne thread at a time
EventA flag one thread sets, another waits for
current_thread()The thread running this line
ThreadPoolExecutorWhat 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.

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.

MistakeWhat you seeWhy it happensFix
0.1 + 0.2 == 0.3FalseFloats are binary approximationsround(..., 2), or math.isclose
-7 // 2 expected to be -3-4Floor division rounds downint(-7 / 2) to round towards zero
(78) meant as a tupleAn intThe comma makes a tuple(78,)
basket=[] as a defaultEvery call shares one listDefaults are made oncebasket=None
if name == NoneWorks, but wrongly for odd typesNone is one objectif name is None
Changing a list while looping itItems skippedThe positions shiftLoop over a copy
except Exception above except ValueErrorThe specific block never runsFirst match winsSpecific first
Opening with w to addThe file is emptiedw truncatesUse a
from module import *Names appear from nowhereEverything was importedImport what you need
A lambda given a nameTracebacks say <lambda>It was created without a nameUse def
Thread(target=fetch())Runs immediately, on this threadThe brackets called ittarget=fetch
self left out of a methodTypeError about argumentsThe object is passed firstdef method(self):

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