The essentials

Quick reference

One focused task per row. Jump to the related section for complete, working examples.

UseSyntaxExamples
Define a classclass Account: passView examples
Initialize instance statedef __init__(self, owner): self.owner = ownerView examples
Define instance behaviordef deposit(self, amount): self.balance += amountView examples
Define shared stateclass Account: currency = 'USD'View examples
Add an alternate constructor@classmethodView examples
Attach a utility@staticmethodView examples
Expose computed state@propertyView examples
Create a subclassclass Admin(User): passView examples
Delegate through the MROsuper().__init__(name)View examples
Test compatible typeisinstance(user, User)View examples
Generate record methods@dataclassView examples
Create a fresh mutable defaulttags: list[str] = field(default_factory=list)View examples
Emulate immutability@dataclass(frozen=True)View examples
Generate slots@dataclass(slots=True)View examples
Convert fields recursivelypayload = asdict(order)View examples
Copy with changesupdated = replace(item, price=12)View examples

Classes combine state with behavior and define a new object type. Keep instance state on self, use class state only when it is genuinely shared, and favor small composable objects over deep inheritance. Dataclasses remove record-like boilerplate but do not replace validation or domain design.

Step by step

Detailed examples

01

Put per-object state on the instance

Creating a class executes its body once and produces a class object. Calling the class creates an instance and invokes __init__ to initialize it. Instance methods are functions retrieved through the instance, which binds that instance as self; self is a convention, but using it consistently makes code readable.

A small account type
class Account:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError('amount must be positive')
        self.balance += amount
        return self.balance

account = Account('Ada', 10)
print(account.deposit(5))
print(account.owner)
Output
15
Ada
Back to quick reference ↑
02

Separate shared defaults from mutable instance state

Attribute lookup checks the instance and then its class hierarchy. Assigning through an instance normally creates or replaces an instance attribute, leaving the class attribute unchanged. Never put a mutable list or dictionary on the class merely as an instance default, because every instance would share it.

Override a shared class attribute
class Report:
    format = 'text'

first = Report()
second = Report()
first.format = 'html'

print(first.format)
print(second.format)
print(Report.format)
Output
html
text
text
Back to quick reference ↑
03

Offer meaningful construction and access APIs

A classmethod is well suited to alternate constructors because cls preserves subclassing. A staticmethod is appropriate only when the operation belongs in the class namespace but needs no object state. A property can preserve attribute-style access for computed or validated values; avoid hiding expensive or surprising work behind it.

Alternate constructor and computed property
class Person:
    def __init__(self, first, last):
        self.first = first
        self.last = last

    @classmethod
    def from_full_name(cls, text):
        first, last = text.split(maxsplit=1)
        return cls(first, last)

    @property
    def full_name(self):
        return f'{self.first} {self.last}'

person = Person.from_full_name('Grace Hopper')
print(person.full_name)
Output
Grace Hopper
Back to quick reference ↑
04

Use inheritance for genuine substitutability

A subclass should satisfy the expectations of code written for its base class. super() follows the method-resolution order, which matters especially with multiple inheritance. Prefer composition when one object merely uses another service; it keeps dependencies explicit and avoids exposing every base-class behavior.

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

    def label(self):
        return self.name

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

admin = Admin('Lin', 'billing')
print(admin.label())
print(isinstance(admin, User))
Output
Lin
True
Back to quick reference ↑
05

Generate boilerplate for record-like classes

@dataclass examines annotated fields and generates an initializer, representation, and equality by default. Type annotations are not enforced at runtime. Field order affects the initializer and comparisons, so treat it as part of the API and put required fields before fields with defaults.

A typed record with a derived method
from dataclasses import dataclass

@dataclass
class LineItem:
    sku: str
    quantity: int
    unit_price: float

    def total(self):
        return self.quantity * self.unit_price

item = LineItem('A-42', 3, 2.5)
print(item)
print(item.total())
Output
LineItem(sku='A-42', quantity=3, unit_price=2.5)
7.5
Back to quick reference ↑
06

Make defaults safe and object guarantees explicit

Use default_factory for mutable defaults so every instance receives a fresh object. frozen=True emulates read-only fields but cannot make referenced mutable objects immutable. slots=True can reduce per-instance storage and prevent arbitrary new attributes; it returns a new slotted class, so review inheritance interactions.

Independent lists and a frozen value
from dataclasses import dataclass, field

@dataclass
class Note:
    text: str
    tags: list[str] = field(default_factory=list)

@dataclass(frozen=True, slots=True)
class Coordinate:
    x: int
    y: int

first = Note('One')
second = Note('Two')
first.tags.append('urgent')
print(first.tags, second.tags)
print(Coordinate(3, 4))
Output
['urgent'] []
Coordinate(x=3, y=4)
Back to quick reference ↑
07

Copy and serialize dataclass values deliberately

replace constructs a new object through the generated initializer, making it useful for frozen records and controlled updates. asdict recursively converts nested dataclasses and containers and deep-copies other objects, which can be more work than a shallow projection. Neither function is a general validation or wire-format layer.

Replace a frozen value and project its fields
from dataclasses import asdict, dataclass, replace

@dataclass(frozen=True)
class Product:
    sku: str
    price: int

original = Product('A-42', 10)
updated = replace(original, price=12)
print(original.price, updated.price)
print(asdict(updated))
Output
10 12
{'sku': 'A-42', 'price': 12}
Back to quick reference ↑

Local code tester

Model a record with behavior

Edit a dataclass, add validation or derived behavior, and create updated immutable copies.

Runs in your browser
Output
Press Run to load Python locally.

Sources and further reading

References

Authoritative documentation used to verify and expand this cheat sheet.

  1. Python Software FoundationPython Tutorial: Classesdocs.python.org
  2. Python Software FoundationData Classesdocs.python.org
  3. Python Software FoundationThe Python Data Modeldocs.python.org
  4. Python Software FoundationBuilt-in Functions: isinstancedocs.python.org

Help us improve

Found a typo or missing example?

Tell us what would make this cheat sheet clearer, more complete, or more useful.

Share feedback