Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. Cheat Sheets
  3. Python
  4. Python OOP Cheat Sheet
PythonCheat Sheet

Python OOP Cheat Sheet

Quick-reference for Python classes and object-oriented patterns. Each section includes copy-ready snippets with inline output comments.

On this page
  1. 1Class Definition and __init__
  2. 2Instance vs Class Attributes
  3. 3Inheritance and super()
  4. 4Dunder Methods (__str__, __repr__, __eq__, __len__)
  5. 5@property (Computed / Validated Attributes)
  6. 6@dataclass (Auto-Generated Classes)
  7. 7Abstract Classes (abc)
  8. 8@classmethod vs @staticmethod
  9. 9Composition vs Inheritance
  10. 10Private Attributes and Name Mangling
  11. 11Common TypeError Fixes
Class Definition and __init__Instance vs Class AttributesInheritance and super()Dunder Methods (__str__, __repr__, __eq__, __len__)@property (Computed / Validated Attributes)@dataclass (Auto-Generated Classes)Abstract Classes (abc)@classmethod vs @staticmethodComposition vs InheritancePrivate Attributes and Name ManglingCommon TypeError Fixes

Class Definition and __init__

__init__ sets up instance attributes when an object is created. self is the instance.

Basic class
class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed

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

fido = Dog('Fido', 'Lab')
fido.bark()   # => 'Fido says woof!'
Default argument in __init__
class Counter:
    def __init__(self, start=0):
        self.count = start

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

c = Counter()
c.increment()
c.count   # => 1

Instance vs Class Attributes

Class attributes are shared across all instances. Instance attributes (self.x) are per-instance.

Class attribute (shared)
class Dog:
    species = 'Canis familiaris'   # class attribute

Dog.species        # => 'Canis familiaris'
Dog().species      # => 'Canis familiaris'  (same object)
The mutable class attribute trap
class Bad:
    items = []   # shared across ALL instances!

a, b = Bad(), Bad()
a.items.append(1)
b.items   # => [1]  (oops, shared!)
Fix: use self in __init__
class Good:
    def __init__(self):
        self.items = []   # each instance gets its own

a, b = Good(), Good()
a.items.append(1)
b.items   # => []  (independent)

Rule: use class attributes for shared constants. Use self.x in __init__ for per-instance state.

Inheritance and super()

Subclasses inherit methods and attributes. Call super().__init__() to run the parent's initialization.

Basic inheritance
class Animal:
    def __init__(self, name):
        self.name = name

    def speak(self):
        return f'{self.name} makes a sound'

class Dog(Animal):
    def speak(self):
        return f'{self.name} barks'

Dog('Rex').speak()   # => 'Rex barks'
super().__init__() in subclass
class Dog(Animal):
    def __init__(self, name, breed):
        super().__init__(name)   # parent sets self.name
        self.breed = breed

d = Dog('Rex', 'Lab')
d.name    # => 'Rex'
d.breed   # => 'Lab'
Forgetting super().__init__()
class Dog(Animal):
    def __init__(self, name, breed):
        self.breed = breed
        # forgot super().__init__(name)!

d = Dog('Rex', 'Lab')
d.name   # AttributeError: 'Dog' has no attribute 'name'
Check inheritance
isinstance(Dog('Rex', 'Lab'), Animal)   # => True
issubclass(Dog, Animal)                 # => True

Dunder Methods (__str__, __repr__, __eq__, __len__)

Special methods (double underscore) let your objects work with Python syntax and built-in functions.

__repr__ (for developers)
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __repr__(self):
        return f'Point({self.x!r}, {self.y!r})'

repr(Point(3, 4))   # => 'Point(3, 4)'
__str__ (for users)
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __str__(self):
        return f'({self.x}, {self.y})'

    def __repr__(self):
        return f'Point({self.x!r}, {self.y!r})'

print(Point(3, 4))   # => '(3, 4)'  (uses __str__)

If only one is defined, implement __repr__. Python uses it as fallback for str().

__eq__ (equality comparison)
class Point:
    def __init__(self, x, y):
        self.x, self.y = x, y

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

Point(1, 2) == Point(1, 2)   # => True
__len__ and __bool__
class Playlist:
    def __init__(self, songs):
        self.songs = songs

    def __len__(self):
        return len(self.songs)

    def __bool__(self):
        return len(self.songs) > 0

p = Playlist(['a', 'b'])
len(p)    # => 2
bool(p)   # => True
__iter__ (make iterable)
class Countdown:
    def __init__(self, start):
        self.start = start

    def __iter__(self):
        n = self.start
        while n > 0:
            yield n
            n -= 1

list(Countdown(3))   # => [3, 2, 1]

@property (Computed / Validated Attributes)

Use @property to add validation or computation behind attribute access syntax.

Read-only computed property
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def area(self):
        return 3.14159 * self.radius ** 2

c = Circle(5)
c.area   # => 78.53975  (no parentheses)
Property with setter (validation)
class Person:
    def __init__(self, age):
        self.age = age   # uses the setter

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError('Age cannot be negative')
        self._age = value

p = Person(25)
# p.age = -1   # ValueError: Age cannot be negative
Property with deleter
class User:
    def __init__(self, name):
        self._name = name

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

    @name.deleter
    def name(self):
        self._name = 'Anonymous'

u = User('Alice')
del u.name
u.name   # => 'Anonymous'

@dataclass (Auto-Generated Classes)

Automatically generates __init__, __repr__, and __eq__. Ideal for data containers.

Basic dataclass
from dataclasses import dataclass

@dataclass
class Point:
    x: float
    y: float

p = Point(1.0, 2.0)
repr(p)                  # => 'Point(x=1.0, y=2.0)'
p == Point(1.0, 2.0)    # => True
Default values and field()
from dataclasses import dataclass, field

@dataclass
class Config:
    host: str = 'localhost'
    port: int = 8080
    tags: list[str] = field(default_factory=list)

Use field(default_factory=list) for mutable defaults. Never use tags: list = [].

Frozen (immutable) dataclass
@dataclass(frozen=True)
class Color:
    r: int
    g: int
    b: int

c = Color(255, 0, 0)
# c.r = 128   # FrozenInstanceError
Slots for memory efficiency (Python 3.10+)
@dataclass(slots=True)
class Vector:
    x: float
    y: float
    z: float

# Uses __slots__ instead of __dict__ for lower memory

Abstract Classes (abc)

Abstract base classes define interfaces. Subclasses must implement all abstract methods.

Define an abstract class
from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        ...

    @abstractmethod
    def perimeter(self):
        ...

# Shape()   # TypeError: Can't instantiate abstract class
Concrete implementation
class Circle(Shape):
    def __init__(self, radius):
        self.radius = radius

    def area(self):
        return 3.14159 * self.radius ** 2

    def perimeter(self):
        return 2 * 3.14159 * self.radius

c = Circle(5)
c.area()        # => 78.53975
c.perimeter()   # => 31.4159
Abstract property
class Shape(ABC):
    @property
    @abstractmethod
    def name(self) -> str:
        ...

@classmethod vs @staticmethod

@classmethod receives cls (the class). @staticmethod receives nothing (just a namespaced function).

@classmethod (alternative constructor)
class Date:
    def __init__(self, year, month, day):
        self.year = year
        self.month = month
        self.day = day

    @classmethod
    def from_string(cls, s):
        y, m, d = map(int, s.split('-'))
        return cls(y, m, d)

d = Date.from_string('2026-02-22')
d.year   # => 2026
@staticmethod (no self or cls)
class MathUtils:
    @staticmethod
    def is_even(n):
        return n % 2 == 0

MathUtils.is_even(4)   # => True
When to use which
# @classmethod: needs access to class (cls)
#   - Alternative constructors (from_json, from_dict)
#   - Factory methods that return instances
#
# @staticmethod: no access to class or instance
#   - Utility functions that belong to the class namespace
#   - Validation helpers

Composition vs Inheritance

Inheritance (is-a)
class Animal:
    def eat(self):
        return 'eating'

class Dog(Animal):   # Dog IS an Animal
    def bark(self):
        return 'woof'

Dog().eat()   # => 'eating'  (inherited)
Composition (has-a)
class Engine:
    def start(self):
        return 'vroom'

class Car:
    def __init__(self):
        self.engine = Engine()   # Car HAS an Engine

    def start(self):
        return self.engine.start()

Car().start()   # => 'vroom'
When to prefer composition
# Prefer composition when:
# - You need to swap implementations
# - You want to avoid inheriting unwanted methods
# - The relationship is "uses" not "is a kind of"
# - Multiple behaviors would require multiple inheritance

Favor composition over inheritance when in doubt. It is more flexible and avoids diamond inheritance problems.

Private Attributes and Name Mangling

Convention: single underscore (_)
class User:
    def __init__(self, name):
        self._name = name   # "private by convention"

u = User('Alice')
u._name   # => 'Alice'  (still accessible, but signals "internal")
Name mangling: double underscore (__)
class User:
    def __init__(self):
        self.__secret = 42

u = User()
# u.__secret       # AttributeError
u._User__secret    # => 42  (name-mangled to _ClassName__attr)

Double underscore triggers name mangling to prevent accidental override in subclasses. Single underscore is preferred for most "private" attributes.

Common TypeError Fixes

Missing self in method definition
class Bad:
    def greet():   # missing self!
        return 'hi'

# Bad().greet()
# TypeError: greet() takes 0 positional arguments but 1 was given
Calling method on class instead of instance
class Dog:
    def bark(self):
        return 'woof'

# Dog.bark()   # TypeError: missing argument 'self'
Dog().bark()   # => 'woof'  (correct)
Mutable default argument in __init__
# Bad: shared default list
class Bad:
    def __init__(self, items=[]):   # shared!
        self.items = items

# Good: use None sentinel
class Good:
    def __init__(self, items=None):
        self.items = items if items is not None else []
Learn Python in Depth
Python Decorators Practice →Python Error Handling Practice →Python Comprehensions Practice →
Warm-up1 / 2

Can you write this from memory?

Write the class header for a class named `Item` (header only, no body)

See Also
Decorators →Error Handling →

Start Practicing Python

Free daily exercises with spaced repetition. No credit card required.

← Back to Python Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.