Introduction to the Python Cheat Sheet

If you’re looking for the most practical Python cheat sheet, you’ve come to the right place. Python is one of the world’s most popular programming languages — loved by beginners for its simplicity and trusted by professionals for its power in web development, data science, artificial intelligence, machine learning, and automation. This Python cheat sheet by Solviyo is your complete reference, covering everything from basic syntax and variables to advanced topics like object-oriented programming, decorators, generators, async, and modules. You can read it online or download the PDF for offline use.

Whether you are a student learning Python for the first time, a developer brushing up for a coding interview, or a data scientist needing a quick reference for daily work, this Python quick reference guide is structured to save you time. It includes ready-to-use examples, copyable code snippets, best practices, and performance notes — all organized into clear sections. Each topic is explained in plain language with practical tips so you can quickly learn Python, revise concepts, or debug faster.

Some of the most searched topics like Python functions, loops, conditionals, data types, string methods, list comprehensions, dictionaries, sets, and classes are covered with examples and tables. Advanced users will find dedicated sections on Python decorators, dataclasses, enums, pattern matching (PEP 634), virtual environments, and dependency management. We also include insights into when to use specific data structures and the subtle differences between class methods, static methods, and instance methods.

For developers preparing for interviews, this page doubles as a Python interview preparation cheat sheet, giving you all the essential Python syntax and tricks in one place. If you prefer offline reading, you can download the Python cheat sheet PDF version for free and keep it handy on your desktop or mobile device. Bookmark this guide now — it’s your ultimate Python quick reference.

Hello Python

Installing

Install Python from the OS package manager or python.org. Example commands:

# Install via apt (Linux)
sudo apt install python3

# macOS (Homebrew)
brew install python

# Windows (winget)
winget install Python.Python.3

# verify with
python3 --version

Running Scripts

Save code as hello.py and run it from the shell:

# hello.py
print("Hello from Solviyo!")

# Run:
python3 hello.py

REPL (Interactive Mode)

Quickly test lines of code in the REPL (use Ctrl+D to exit):

python3
>>> # type expressions and press Enter
>>> 2 + 3
5
>>> print("REPL test - Solviyo")
REPL test - Solviyo

Comments & Documentation

Inline Comments

Use # for single-line comments and short notes. Put explanatory comments where helpful.

# add two numbers
result = 5 + 3

Docstrings

Use triple-quoted strings for function/class/module documentation. Docstrings are accessible via .__doc__ or help().

def greet(name: str) -> str:
    """Return a greeting.

    Example:
        >>> greet("Solviyo")
        'Hello, Solviyo!'
    """
    return f"Hello, {name}"

Typing Annotations

Type hints clarify intent and help tooling (linters, autocomplete). They are not enforced at runtime by default.

def add(x: int, y: int) -> int:
    """Add two integers (used in Solviyo examples)."""
    return x + y

# Example
result: int = add(2, 3)
Note: Type hints improve readability and work with tools like mypy, IDEs, and language servers.

Variables & Data Types

Python variables don’t need explicit declaration — they’re created when you assign a value.

# Solviyo example variables
x = 42          # int
pi = 3.14159    # float
name = "Solviyo" # string
is_active = True # boolean
nothing = None   # special "no value"

Type Casting

Convert between types using built-in functions:

x = int("5")       # string → int
y = float(10)      # int → float
z = str(99)        # int → string
flag = bool(0)     # → False
check = bool(123)  # → True
Note: Use type() to check the data type: type(name).

Operators

Arithmetic Operators

a, b = 7, 3
print(a + b)  # 10 (addition)
print(a - b)  # 4  (subtraction)
print(a * b)  # 21 (multiplication)
print(a / b)  # 2.333... (division)
print(a // b) # 2  (floor division)
print(a % b)  # 1  (modulus)
print(a ** b) # 343 (exponentiation)

Comparison Operators

a, b = 5, 10
print(a == b)  # False
print(a != b)  # True
print(a > b)   # False
print(a < b)   # True
print(a >= 5)  # True
print(b <= 10) # True

Logical Operators

x, y = True, False
print(x and y) # False
print(x or y)  # True
print(not x)   # False

Bitwise Operators

Operate on the binary representation of integers.

a, b = 6, 3  # (110, 011 in binary)
print(a & b)  # 2  (AND)
print(a | b)  # 7  (OR)
print(a ^ b)  # 5  (XOR)
print(~a)     # -7 (NOT)
print(a << 1) # 12 (left shift)
print(a >> 1) # 3  (right shift)

Membership Operators

text = "Solviyo"
print("S" in text)   # True
print("z" not in text) # True

Identity Operators

Check if two variables point to the same object in memory.

x = [1, 2, 3]
y = x
z = [1, 2, 3]

print(x is y)      # True (same object)
print(x is z)      # False (same value, different object)
print(x is not z)  # True

Input & Output

Python provides simple but powerful ways to interact with users and display results.

Printing Output

# Basic printing
print("Hello Solviyo")

# Print multiple values (separated by space by default)
print("Pi =", 3.14159)

# Customize separator and end
print("A", "B", "C", sep="-", end="*")
# Output: A-B-C*

User Input

input() reads a line from standard input and always returns a string.

name = input("Enter your name: ")
print("Welcome,", name)

age = int(input("Enter your age: "))
print("Next year you'll be", age + 1)

Formatted Strings

Method Example Output
f-string (recommended) f"Hello {name}" Hello Solviyo
str.format() "Pi is {:.2f}".format(3.14159) Pi is 3.14
Old style "%s is %d" % ("Age", 25) Age is 25
name = "Solviyo"
score = 95
print(f"{name} scored {score}/100")

File Redirection (Shell)

You can redirect program output to a file or read input from a file in the shell:

Command Description
python3 prog.py > out.txt Redirect program output to out.txt
python3 prog.py < in.txt Use in.txt as input for the program
python3 prog.py > out.txt 2>&1 Redirect both output and errors to out.txt

Strings

Slicing

text = "Solviyo"
print(text[0])     # S (first character)
print(text[-1])    # o (last character)
print(text[0:4])   # Solv
print(text[:4])    # Solv
print(text[4:])    # iyo
print(text[::2])   # Slv (every 2nd char)

Common Methods

Method Example Output
.lower() "Solviyo".lower() solviyo
.upper() "solviyo".upper() SOLVIYO
.strip() " hi ".strip() hi
.replace() "abc".replace("a","z") zbc
.split() "a,b,c".split(",") ['a','b','c']
.join() "-".join(["a","b"]) a-b

f-Strings

name = "Solviyo"
score = 99
print(f"{name} scored {score}/100")

pi = 3.14159
print(f"Pi rounded: {pi:.2f}")

Regex Basics

Python’s re module enables pattern matching.

import re

text = "Solviyo has 2 cats and 3 dogs"
nums = re.findall(r"\d+", text)  # find all numbers
print(nums)  # ['2', '3']

if re.search(r"cats", text):
    print("Found 'cats'!")

Lists

Creation & Slicing

items = [1, 2, 3, "Solviyo", True]
print(items[0])     # 1
print(items[-1])    # True
print(items[1:4])   # [2, 3, 'Solviyo']

Common Methods

Method Example Effect
.append() nums.append(4) Adds element at end
.extend() nums.extend([5,6]) Adds multiple elements
.insert() nums.insert(1, 99) Insert at index
.remove() nums.remove(99) Remove first match
.pop() nums.pop() Remove & return last element
.sort() nums.sort() Sort in place
.reverse() nums.reverse() Reverse in place

List Comprehensions

# Squares from 0 to 9
squares = [x**2 for x in range(10)]
print(squares)

# Filter even numbers
evens = [x for x in range(10) if x % 2 == 0]
print(evens)

Tuples

Tuples are like lists, but immutable — once created, they cannot be changed. This makes them faster and safe for fixed collections.

Immutability

t = (1, 2, 3)
print(t[0])       # 1
# t[0] = 99       # ❌ Error: 'tuple' object does not support item assignment

Packing & Unpacking

# Packing
point = (10, 20)

# Unpacking
x, y = point
print(x, y)  # 10 20

# Extended unpacking
a, *b = (1, 2, 3, 4)
print(a)  # 1
print(b)  # [2, 3, 4]

Named Tuples

For better readability, use namedtuple (from collections).

from collections import namedtuple

Point = namedtuple("Point", ["x", "y"])
p = Point(10, 20)

print(p.x, p.y)      # 10 20
print(p[0], p[1])    # still works like a tuple

Sets

Sets store unique values. Order is not guaranteed, and duplicates are automatically removed.

Creation

numbers = {1, 2, 3, 3, 2}
print(numbers)  # {1, 2, 3}

empty = set()   # correct way to make empty set

Set Operations

Operation Example Result
Union {1,2} | {2,3} {1,2,3}
Intersection {1,2} & {2,3} {2}
Difference {1,2} - {2,3} {1}
Symmetric Difference {1,2} ^ {2,3} {1,3}

Frozen Sets

frozenset is an immutable set — useful as dictionary keys or when you want fixed unique values.

fs = frozenset([1, 2, 2, 3])
print(fs)        # frozenset({1, 2, 3})
# fs.add(4)      # ❌ Error: can't modify frozenset

Dictionaries

Dictionaries store data as key–value pairs. Keys must be unique and immutable (like strings, numbers, tuples), while values can be anything.

Creation

# Literal
user = {"name": "Alice", "age": 25}

# Constructor
data = dict(x=10, y=20)

print(user["name"])  # Alice

Common Methods

Method Example Result
.keys() user.keys() dict_keys(['name','age'])
.values() user.values() dict_values(['Alice',25])
.items() user.items() dict_items([('name','Alice'),('age',25)])
.get() user.get("name") Alice
.update() user.update({"age": 26}) {'name':'Alice','age':26}
.pop() user.pop("age") 25 (removes key)

Dict Comprehensions

# Square numbers into dict
squares = {x: x**2 for x in range(5)}
print(squares)  # {0:0, 1:1, 2:4, 3:9, 4:16}

Nested Dictionaries

users = {
    "alice": {"age": 25, "role": "admin"},
    "bob": {"age": 30, "role": "editor"}
}

print(users["alice"]["role"])  # admin

Data Structure Comparisons

Python gives us multiple built-in containers. Each has its strengths — choosing the right one can make your code faster and cleaner.

Structure Key Properties Performance Notes Best Use Cases
List Ordered, mutable, allows duplicates Fast append & iteration, slower lookups (O(n)) General sequences, dynamic collections
Tuple Ordered, immutable Lighter & faster than lists, hashable (usable as dict keys) Fixed data, function returns, safe read-only data
Set Unordered, unique values Fast membership checks (O(1)), no duplicates Removing duplicates, fast lookups, math-like operations
Dict Key–value mapping, keys unique Fast key lookups & updates (O(1)) Configurations, mappings, JSON-like data
👉 Rule of thumb: - Use lists when order matters. - Use tuples when data must not change. - Use sets for uniqueness or quick membership tests. - Use dictionaries for labeled, structured data.

Conditionals

Conditionals let your code make decisions. Python uses if, elif, and else blocks, indented consistently.

x = 10

if x > 0:
    print("Positive")
elif x == 0:
    print("Zero")
else:
    print("Negative")

Ternary Operator

For short decisions, Python has a one-liner form:

age = 20
status = "Adult" if age >= 18 else "Minor"
print(status)  # Adult
Tip: Don’t overuse ternary operators — they’re best when expressions are short and readable.

Loops

Loops let you repeat code. Python has two main loop types: for and while.

for loop

Iterates directly over a sequence (no need for indexes unless you want them).

fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)

while loop

Runs as long as a condition is True.

count = 3
while count > 0:
    print(count)
    count -= 1
# prints: 3, 2, 1

range()

The range() function is commonly used with for loops to generate numbers.

for i in range(2, 10, 2):  # start=2, stop=10, step=2
    print(i)
# Output: 2, 4, 6, 8
👉 Use for when looping over items, 👉 Use while when looping until a condition is met.

Loop Utilities

Python adds helpers that make looping cleaner and more expressive.

enumerate()

Gives both index and value when looping through a sequence.

names = ["Alice", "Bob", "Charlie"]

for idx, name in enumerate(names, start=1):
    print(idx, name)
# 1 Alice
# 2 Bob
# 3 Charlie

zip()

Combines multiple sequences element by element.

scores = [90, 85, 78]
for name, score in zip(names, scores):
    print(f"{name}: {score}")
# Alice: 90
# Bob: 85
# Charlie: 78

any() and all()

Check conditions across sequences.

nums = [2, 4, 6, 8]

print(any(n % 2 != 0 for n in nums))  # False
print(all(n % 2 == 0 for n in nums))  # True

Loop Control

Special keywords change how loops behave.

for i in range(5):
    if i == 2:
        continue   # skip iteration
    if i == 4:
        break      # exit loop
    print(i)
else:
    print("Loop finished without break")
Keyword Meaning
break Exit the loop immediately
continue Skip the rest of the current iteration
else Runs only if loop wasn’t broken out of

Pattern Matching (PEP 634)

Python 3.10 introduced match/case for structural pattern matching, similar to switch statements in other languages but more powerful.

def handle_point(pt):
    match pt:
        case (0, 0):
            return "Origin"
        case (x, 0):
            return f"X-axis at {x}"
        case (0, y):
            return f"Y-axis at {y}"
        case (x, y):
            return f"Point ({x}, {y})"
        case _:
            return "Unknown"

print(handle_point((0, 0)))   # Origin
print(handle_point((5, 0)))   # X-axis at 5
👉 match/case works with numbers, strings, sequences, and even classes. 👉 Always include case _: as a fallback.

Defining Functions

Functions let you group reusable logic. Use def followed by a name, parameters, and an optional return statement.

def greet(name):
    return f"Hello, {name}!"

print(greet("Solviyo"))  # Hello, Solviyo!

Arguments

Python functions are flexible in how they accept arguments. Here’s a quick overview:

Type Example Notes
Positional power(2, 3) Order matters
Keyword power(base=2, exp=3) Order doesn’t matter
Default def f(x=10) Value used if no argument passed
*args def f(*nums) Collects extra positional args (tuple)
**kwargs def f(**opts) Collects extra keyword args (dict)
def demo(*args, **kwargs):
    print("args:", args)
    print("kwargs:", kwargs)

demo(1, 2, 3, a=10, b=20)
# args: (1, 2, 3)
# kwargs: {'a': 10, 'b': 20}

Scope & Closures

Python resolves variable names using the LEGB rule: Local → Enclosing → Global → Built-in.

Scope Description
Local Names inside the current function
Enclosing Names in outer (non-global) functions
Global Names at the module level
Built-in Names preloaded by Python (e.g. len)

Examples

x = "global"

def outer():
    msg = "enclosing"
    def inner():
        nonlocal msg
        print(x, msg)
    inner()

outer()  # global enclosing
👉 global lets you modify a module-level variable. 👉 nonlocal lets you modify a variable in an enclosing (but not global) scope. 👉 Closures happen when an inner function remembers variables from its enclosing scope.

Anonymous Functions → Lambdas

Use lambda for short, throwaway functions. Best for one-liners where a full def would be overkill.

square = lambda x: x**2
print(square(5))  # 25

words = ["solviyo", "python", "cheatsheet"]
print(sorted(words, key=lambda w: len(w)))
# ['python', 'solviyo', 'cheatsheet']
👉 Lambdas are limited to a single expression. Use def for complex logic.

Decorators

Decorators wrap one function with another, adding extra behavior.

Decorator Usage
@staticmethod Bind method to class, no self
@classmethod Bind method to class, gets cls
@property Define getter/setter-like methods
def logger(func):
    def wrapper(*args, **kwargs):
        print(f"[Solviyo Log] Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@logger
def greet(name):
    return f"Hello {name}"

print(greet("Python"))

Generators & Iterators

Generators produce items lazily using yield. Iterators expose __iter__() and __next__().

def countdown(n):
    while n > 0:
        yield n
        n -= 1

for num in countdown(3):
    print(num)
# 3 2 1
nums = [10, 20, 30]
it = iter(nums)

print(next(it))  # 10
print(next(it))  # 20
👉 Generators save memory — they yield one value at a time instead of building the whole list in memory.

Built-in Functional Tools

Python provides built-ins for applying functions across iterables.

Function Usage
map(func, iterable) Apply func to each item
filter(func, iterable) Keep items where func returns True
reduce(func, iterable) Accumulate to a single value (from functools)
zip(*iterables) Combine items element-wise
from functools import reduce

nums = [1, 2, 3, 4]
print(list(map(lambda x: x*2, nums)))     # [2, 4, 6, 8]
print(list(filter(lambda x: x%2==0, nums)))  # [2, 4]
print(reduce(lambda a,b: a+b, nums))      # 10
print(list(zip("abc", nums)))             # [('a',1),('b',2),('c',3)]

Imports

Python imports can be absolute, relative, or use aliases.

Type Example Notes
Absolute import math Most common; full path
Relative from . import utils Within packages; uses dots
Aliasing import numpy as np Shorter reference name
Selective from math import sqrt Import only what you need

Built-in Modules

Python comes batteries-included. Here are some must-know modules:

Module Use Case Example
math Mathematical functions math.sqrt(16) → 4.0
random Random numbers random.choice([1,2,3])
datetime Dates & times datetime.date.today()
os File system, env vars os.listdir(".")
sys Python runtime info sys.version
import math, random, datetime

print(math.factorial(5))         # 120
print(random.randint(1, 10))     # random int
print(datetime.date.today())     # current date

Creating Packages

A package is just a folder with an __init__.py file.

my_project/
│
├── app.py
├── utils/
│   ├── __init__.py
│   ├── math_tools.py
│   └── string_tools.py
# app.py
from utils import math_tools

print(math_tools.add(2, 3))

Virtual Environments

Keep dependencies isolated per project using venv.

python -m venv venv
source venv/bin/activate   # macOS/Linux
venv\Scripts\activate      # Windows
👉 When active, pip install installs into the venv, not globally.

Dependency Management

Two common ways to lock dependencies:

File Purpose Example
requirements.txt Pin exact versions requests==2.31.0
pyproject.toml Modern build metadata [tool.poetry.dependencies]
# Freeze dependencies
pip freeze > requirements.txt

# Install dependencies
pip install -r requirements.txt

Classes & Objects

In Python, everything is an object. Classes define blueprints for creating objects, bundling attributes (data) and methods (behavior).

Basic Syntax

class Dog:
    # class attribute
    species = "Canis familiaris"

    def __init__(self, name, age):
        # instance attributes
        self.name = name
        self.age = age

    def bark(self):
        return f"{self.name} says woof!"

# create objects
fido = Dog("Fido", 3)
print(fido.bark())  # Fido says woof!

Attributes & Methods

Type Defined Example
Class Attribute Inside class, shared by all objects species
Instance Attribute Inside __init__, unique to each object self.name, self.age
Instance Method Defined with def, uses self bark()

Adding & Accessing Attributes

# add a new attribute dynamically
fido.color = "brown"
print(fido.color)  # brown

# access class attribute
print(Dog.species)  # Canis familiaris
👉 Use self to access instance data. 👉 Objects can have attributes added at runtime. 👉 Class attributes are shared unless shadowed by an instance attribute.

Inheritance

Inheritance lets a class (child) reuse and extend behavior from another class (parent).

Single Inheritance

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

class Dog(Animal):
    def speak(self):
        return "Woof!"

dog = Dog()
print(dog.speak())  # Woof!

Multiple Inheritance

class Walker:
    def move(self):
        return "Walking"

class Swimmer:
    def move(self):
        return "Swimming"

class Amphibian(Walker, Swimmer):
    pass

frog = Amphibian()
print(frog.move())  # Walking (first base class wins)
👉 Python resolves method lookups using the MRO (Method Resolution Order). 👉 In Amphibian(Walker, Swimmer), Python searches in Walker → Swimmer → object.

Using super()

super() lets child classes call methods from their parent without naming it directly.

class Vehicle:
    def __init__(self, brand):
        self.brand = brand

class Car(Vehicle):
    def __init__(self, brand, model):
        super().__init__(brand)  # call parent __init__
        self.model = model

car = Car("Tesla", "Model S")
print(car.brand, car.model)  # Tesla Model S

Types of Inheritance

Type Description Example
Single One base class, one child class Dog(Animal)
Multiple Child inherits from multiple parents Amphibian(Walker, Swimmer)
Multilevel Child → Parent → Grandparent chain C → B → A
Hierarchical Multiple children from one parent Dog(Animal), Cat(Animal)

Polymorphism

Polymorphism allows different classes to define methods with the same name, but different behavior. This makes code more flexible and extensible.

Method Overriding

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

class Dog(Animal):
    def speak(self):  # overrides parent method
        return "Woof!"

class Cat(Animal):
    def speak(self):  # overrides parent method
        return "Meow!"

animals = [Dog(), Cat()]
for a in animals:
    print(a.speak())  
    # Woof!
    # Meow!

Built-in Example

Python’s built-in functions also show polymorphism. The same len() works on strings, lists, tuples, and more:

print(len("Solviyo"))    # 7 (string length)
print(len([1, 2, 3]))    # 3 (list length)
print(len((10, 20)))     # 2 (tuple length)

Key Points

Concept Description Example
Overriding Child class redefines a parent method Dog.speak()
Same Interface Different objects respond to the same method a.speak() → Dog or Cat
Built-in Polymorphism Functions like len(), iter() len("abc"), len([1,2,3])
👉 Polymorphism is not limited to class hierarchies — Python’s duck typing means “if it walks like a duck and quacks like a duck, it’s a duck.” 👉 As long as objects implement the required methods, they can be used interchangeably.

Encapsulation

Encapsulation restricts direct access to an object’s data. In Python, this is done by naming conventions rather than enforced rules.

Public, Protected, Private

class Account:
    def __init__(self, user, balance):
        self.user = user          # public
        self._balance = balance   # protected (convention)
        self.__pin = "1234"       # private (name mangling)

    def get_balance(self):
        return f"{self.user}'s balance: {self._balance}"

    def __get_pin(self):  # private method
        return self.__pin

acc = Account("SolviyoUser", 500)

print(acc.user)        # accessible
print(acc._balance)    # accessible but discouraged
# print(acc.__pin)     # AttributeError
print(acc._Account__pin)  # accessible via name mangling (not recommended)

Access Levels

Level Convention Accessibility
Public variable Fully accessible anywhere
Protected _variable Accessible, but meant for internal use only
Private __variable Class-internal use (name mangled)
👉 Python relies on developer discipline for encapsulation — unlike Java or C++. 👉 Use getters/setters only when needed (validation, computed properties). 👉 @property is Python’s idiomatic way to manage controlled access.

Using @property

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

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

    @name.setter
    def name(self, value):  # setter
        if not value:
            raise ValueError("Name cannot be empty")
        self._name = value

u = User("Solviyo")
print(u.name)   # Solviyo
u.name = "AI"   # valid update

Special Methods (Dunder Methods)

Special methods in Python start and end with __ (double underscores). They let you customize how objects behave with built-in functions and operators.

Example: Custom Class with Special Methods

class Book:
    def __init__(self, title, author, pages):
        self.title = title
        self.author = author
        self.pages = pages

    def __str__(self):   # human-readable
        return f"{self.title} by {self.author}"

    def __repr__(self):  # developer-friendly
        return f"Book({self.title!r}, {self.author!r}, {self.pages})"

    def __len__(self):   # works with len()
        return self.pages

    def __eq__(self, other):  # ==
        return self.title == other.title

b1 = Book("Python Tricks", "Solviyo", 250)
b2 = Book("Python Tricks", "Solviyo", 250)

print(str(b1))    # Python Tricks by Solviyo
print(repr(b1))   # Book('Python Tricks', 'Solviyo', 250)
print(len(b1))    # 250
print(b1 == b2)   # True

Common Special Methods

Method Purpose Example
__init__ Constructor, initialize attributes Book("Title", "Author", 100)
__str__ User-friendly string print(obj)
__repr__ Developer string (debugging) obj in REPL
__len__ Called by len() len(obj)
__eq__ Equality check obj1 == obj2
__add__ Overload + operator obj1 + obj2
__iter__, __next__ Make class iterable for x in obj
__enter__, __exit__ Context managers with obj:
👉 __str__ is for end users, __repr__ is for developers. 👉 If you only define __repr__, it’s often used as fallback for __str__. 👉 Special methods let your classes feel “native” in Python.

Class vs Static Methods

Besides normal instance methods, Python supports class methods and static methods. These are defined using decorators and control how methods access data.

Example

class MathUtils:
    pi = 3.14159

    def area_of_circle(self, r):  # instance method
        return self.pi * r * r

    @classmethod
    def get_pi(cls):  # class method
        return cls.pi

    @staticmethod
    def add(x, y):  # static method
        return x + y

m = MathUtils()

# Instance method (needs object)
print(m.area_of_circle(5))  # 78.53975

# Class method (works via class or object)
print(MathUtils.get_pi())   # 3.14159

# Static method (no access to class/instance data)
print(MathUtils.add(3, 4))  # 7

Comparison

Method Type Decorator First Arg Access Use Case
Instance None self Instance attributes & class attributes Object-specific behavior
Class @classmethod cls Class attributes (shared across instances) Factory methods, alternative constructors
Static @staticmethod None No direct access Utility functions logically tied to class
👉 @classmethod is great for alternative constructors (e.g., Date.from_string("2025-09-28")). 👉 @staticmethod is just a namespaced function inside the class. 👉 Regular instance methods remain the most common.

Dataclasses & Enums

Python provides dataclasses (for simpler class boilerplate) and enums (for named constants). These improve readability and reduce manual code.

Dataclasses

from dataclasses import dataclass

@dataclass
class Book:
    title: str
    author: str
    pages: int = 0

b1 = Book("Python 101", "Solviyo", 250)
print(b1)  # Book(title='Python 101', author='Solviyo', pages=250)

Frozen Dataclasses

Frozen dataclasses make instances immutable:

@dataclass(frozen=True)
class Config:
    api_key: str

cfg = Config("XYZ-123")
# cfg.api_key = "ABC"  # ❌ Error: cannot assign to field

Dataclass Benefits

Feature Normal Class Dataclass
Auto __init__ Manual Generated automatically
Auto __repr__ Manual Generated automatically
Immutability Manual @property frozen=True
👉 Use dataclasses when you need lightweight data containers. 👉 Frozen dataclasses are useful for config objects, keys, and constants.

Enums

from enum import Enum, auto

class Status(Enum):
    PENDING = auto()
    ACTIVE = auto()
    INACTIVE = auto()

print(Status.ACTIVE)       # Status.ACTIVE
print(Status.ACTIVE.name)  # ACTIVE
print(Status.ACTIVE.value) # 2

Enum Use Cases

Scenario Why Enums Help
Status Flags Avoids using raw strings ("active", "inactive")
Constants Groups related constants in one class
Readability Self-documenting & safer than magic numbers
👉 Enums are iterable, so you can loop over members with for status in Status. 👉 Use auto() for automatic numbering. 👉 Great for states, categories, or configuration flags.

Comprehensions & Context Managers

Comprehensions provide a compact way to build collections, while context managers simplify resource handling. Both improve readability and reduce boilerplate.

List, Dict, Set, and Nested Comprehensions

# List comprehension
squares = [x**2 for x in range(6)]
# [0, 1, 4, 9, 16, 25]
# Dict comprehension
square_dict = {x: x**2 for x in range(6)}
# {0:0, 1:1, 2:4, 3:9, 4:16, 5:25}
# Set comprehension
unique_lengths = {len(word) for word in ["solviyo", "python", "ai", "python"]}
# {2, 6, 7}
# Nested comprehension (matrix)
matrix = [[i*j for j in range(3)] for i in range(3)]
# [[0, 0, 0], [0, 1, 2], [0, 2, 4]]

When to Use Comprehensions

Case Use Loop Use Comprehension
Complex logic ✔️ Clearer with if/else blocks ❌ Can reduce readability
Simple transformation ❌ Verbose ✔️ Concise & faster
Readability ✔️ For beginners ✔️ For short, clean expressions
👉 Prefer comprehensions for simple, one-line transformations. 👉 Avoid nesting too deeply — readability matters! 👉 Great for filtering, mapping, and lightweight data reshaping.

Context Managers

# Built-in context manager
with open("solviyo.txt", "w") as f:
    f.write("Python Cheat Sheets are awesome!")
# Custom context manager
class SolviyoLogger:
    def __enter__(self):
        print("Start logging...")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("Stop logging...")

with SolviyoLogger():
    print("Inside the block")
👉 with ensures cleanup automatically (like closing files). 👉 Use custom context managers for logging, locks, or resources. 👉 Reduces chances of resource leaks and errors.

Advanced Python Modules

Python’s standard library provides powerful built-in modules for data processing and performance. Here we explore itertools, functools, and collections with practical examples.

Itertools Module

itertools helps with efficient looping and combinatorial problems.

from itertools import combinations, permutations, product

# Combinations (order doesn't matter)
print(list(combinations([1,2,3], 2)))
# [(1, 2), (1, 3), (2, 3)]

# Permutations (order matters)
print(list(permutations([1,2,3], 2)))
# [(1, 2), (1, 3), (2, 1), (2, 3), (3, 1), (3, 2)]

# Cartesian product
print(list(product([1,2], ['a','b'])))
# [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]
👉 Use combinations for subsets, permutations when order matters, and product for multi-set pairing. 👉 Extremely useful in algorithms, games, and test case generation.

Functools Module

functools provides tools for higher-order functions and performance optimization.

from functools import lru_cache, partial, reduce

# lru_cache for memoization
@lru_cache(maxsize=100)
def fib(n):
    if n < 2: return n
    return fib(n-1) + fib(n-2)

print(fib(30))  # Cached calls improve performance

# partial: pre-filling arguments
def power(base, exp):
    return base ** exp

square = partial(power, exp=2)
print(square(5))  # 25

# reduce: cumulative function
nums = [1, 2, 3, 4]
total = reduce(lambda x, y: x + y, nums)
print(total)  # 10
👉 lru_cache improves recursive performance. 👉 partial helps create specialized versions of functions. 👉 reduce applies a function cumulatively (like sum/product).

Collections Module

collections offers specialized containers beyond dicts and lists.

from collections import Counter, defaultdict, deque, OrderedDict

# Counter
cnt = Counter("solviyo loves python")
print(cnt.most_common(3))  # [('o', 3), (' ', 2), ('s', 2)]

# defaultdict
dd = defaultdict(int)
dd['missing'] += 1
print(dd)  # defaultdict(, {'missing': 1})

# deque (fast appends & pops from both ends)
dq = deque([1,2,3])
dq.appendleft(0)
print(dq)  # deque([0,1,2,3])

# OrderedDict (preserves insertion order, Python 3.6+ dict does too)
od = OrderedDict()
od['a'] = 1
od['b'] = 2
print(list(od.keys()))  # ['a','b']
Class Use Case
Counter Counting frequencies (e.g., words, items)
defaultdict Avoids KeyError, auto-creates default values
deque Fast queue/stack operations on both ends
OrderedDict Preserve insertion order (historically before dict did)
👉 Use collections when default Python containers feel limited. 👉 Counter is excellent for quick statistics. 👉 deque outperforms lists for frequent appends/pops from both ends.

Typing, Async & Metaprogramming

Modern Python supports type hints for clarity, async/await for concurrency, and metaprogramming for dynamic code. These advanced tools help make code more reliable, scalable, and flexible.

Typing & Type Hints

Type hints improve readability, catch bugs earlier (via tools like mypy), and enable better IDE support.

from typing import List, Union, Optional

def greet(name: str, age: Optional[int] = None) -> str:
    if age:
        return f"Hello {name}, you are {age} years old."
    return f"Hello {name}!"

def add(values: List[int]) -> int:
    return sum(values)

def combine(x: Union[int, str], y: Union[int, str]) -> str:
    return str(x) + str(y)
👉 Union = multiple possible types. 👉 Optional[X] = either X or None. 👉 List[int] = generics notation.

Async & Await

Python uses async and await for concurrent programming with an event loop. Ideal for I/O-bound tasks like web requests, DB queries, or file operations.

import asyncio

async def fetch_data():
    print("Fetching data...")
    await asyncio.sleep(2)
    return {"source": "Solviyo", "data": [1,2,3]}

async def main():
    result = await fetch_data()
    print(result)

asyncio.run(main())
Concept Description
async def Defines a coroutine function
await Pauses until coroutine/task is complete
asyncio.run() Runs an event loop until finished
👉 Best for network calls, APIs, and parallel I/O. 👉 Avoid for CPU-heavy tasks (use multiprocessing instead).

Metaprogramming

Metaprogramming lets Python code generate or modify code dynamically. Use with caution for advanced use cases.

# type(): create classes dynamically
MyClass = type("MyClass", (), {"x": 42})
obj = MyClass()
print(obj.x)  # 42

# Metaclasses: control class creation
class Meta(type):
    def __new__(cls, name, bases, attrs):
        attrs["tag"] = "CreatedBySolviyo"
        return super().__new__(cls, name, bases, attrs)

class Demo(metaclass=Meta):
    pass

print(Demo.tag)  # CreatedBySolviyo

# eval / exec: run code strings (use cautiously!)
code = "print('Hello from exec at Solviyo!')"
exec(code)
👉 type() can build classes on the fly. 👉 Metaclasses are advanced — great for frameworks & ORMs. 👉 eval/exec are powerful but risky — avoid with untrusted input.

Files & Persistence

Python provides powerful tools for working with files and structured data formats like JSON and CSV. Proper handling ensures reliability and prevents resource leaks.

File Handling

Use the built-in open() function for reading and writing files. Always prefer context managers (with) for automatic closing.

# Writing to a file
with open("solviyo_notes.txt", "w") as f:
    f.write("Learning Python with Solviyo!")

# Reading from a file
with open("solviyo_notes.txt", "r") as f:
    content = f.read()
    print(content)

JSON Handling

JSON (JavaScript Object Notation) is widely used for data exchange. Python’s json module makes serialization and deserialization simple.

import json

# Writing JSON
data = {"site": "Solviyo", "language": "Python"}
with open("data.json", "w") as f:
    json.dump(data, f)

# Reading JSON
with open("data.json", "r") as f:
    loaded = json.load(f)
    print(loaded)

CSV Handling

CSV (Comma-Separated Values) is common for tabular data. Use the csv module for reading and writing.

import csv

# Writing CSV
rows = [["Name", "Score"], ["Alice", 90], ["Bob", 85]]
with open("scores.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(rows)

# Reading CSV
with open("scores.csv", "r") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

When to Use

Format Use Case
Plain Text Simple notes, logs, or unstructured data
JSON APIs, configs, structured but lightweight data
CSV Tabular data, spreadsheets, interoperability
👉 Always use context managers (with) to prevent memory leaks. 👉 For large JSON/CSV files, consider streaming or chunked reading. 👉 Ensure file encoding (UTF-8 recommended) for cross-platform compatibility.

Error Handling & Debugging

Python uses exceptions to signal errors. Understanding how to handle and raise exceptions properly makes your programs more reliable and easier to debug.

Try / Except / Else / Finally

Wrap risky code in try. Use except for handling, else for success cases, and finally for cleanup.

try:
    num = int("10a")
except ValueError:
    print("Invalid number!")
else:
    print("Conversion successful")
finally:
    print("Execution finished")

Common Exceptions

Python raises built-in exceptions for common errors. Some useful ones include:

Exception When it Occurs
ValueError Wrong value (e.g., int("abc"))
TypeError Invalid operation on data types ("2" + 2)
KeyError Accessing missing dict keys
IndexError Index out of range in a list
ZeroDivisionError Dividing by zero

Raising Exceptions

Use raise to signal errors intentionally. You can also create custom exceptions by subclassing Exception.

# Raising a built-in exception
def divide(a, b):
    if b == 0:
        raise ZeroDivisionError("Division by zero is not allowed")
    return a / b

# Custom exception
class SolviyoError(Exception):
    pass

def check_value(x):
    if x < 0:
        raise SolviyoError("Negative values not supported")

check_value(-5)
👉 Use specific exceptions (ValueError, KeyError) instead of a bare except. 👉 Add descriptive messages when raising errors. 👉 Keep custom exceptions meaningful and project-specific.

Practice Python After Reading

Now that you’ve explored the Python Cheat Sheet, test your knowledge and sharpen your skills: