Yes, you can put a class in a class in Python. Define the inner class inside the outer class body, and reach it as Outer.Inner. It’s called a nested or inner class, and it groups a helper type with its owner:
class Store:
class Address:
...
Store.Address("120 Pine St", "Portland", "OR")
Each example was saved as a .py file and run with Python 3.12.5; the screenshots are the actual Windows Command Prompt windows.
Below, I’ll build a nested class, show the scope rule that causes most NameErrors, explain how an inner class can reach the outer object, and cover nested enums and dataclasses.
How to define a class inside a class in Python
Indent the inner class under the outer one, like a method. From inside the outer class you write Store.Address, and from outside it works exactly the same way:
class Store:
class Address:
def __init__(self, street, city, state):
self.street, self.city, self.state = street, city, state
def label(self):
return f"{self.street}, {self.city}, {self.state}"
def __init__(self, name, street, city, state):
self.name = name
self.address = Store.Address(street, city, state)
shop = Store("Corner Books", "120 Pine St", "Portland", "OR")
print(shop.name, "-", shop.address.label())
home = Store.Address("9 Elm Ave", "Tulsa", "OK") # create one from outside
print(home.label())
print(type(home).__qualname__)
Output:
Corner Books - 120 Pine St, Portland, OR
9 Elm Ave, Tulsa, OK
Store.Address

Store.Address.Nesting doesn’t make Address private. It just puts the name inside the Store namespace, so nobody confuses it with some other Address class.
Why does a nested class give a NameError?
Here’s the catch: names defined in a class body aren’t visible inside its methods. So a bare Item(...) inside a method fails, even though Item is right there:
class Order:
class Item:
def __init__(self, name, price):
self.name, self.price = name, price
def __init__(self):
self.items = []
def add_wrong(self, name, price):
self.items.append(Item(name, price)) # bare name: not in scope here
def add(self, name, price):
self.items.append(self.Item(name, price)) # or Order.Item(...)
order = Order()
try:
order.add_wrong("Coffee", 4.50)
except NameError as err:
print("NameError:", err)
order.add("Coffee", 4.50)
order.add("Bagel", 2.75)
print([i.name for i in order.items], f"${sum(i.price for i in order.items):.2f}")
Output:
NameError: name 'Item' is not defined
['Coffee', 'Bagel'] $7.25

Item alone raises NameError. self.Item or Order.Item finds it.Methods skip the class scope when they look up names; they go straight from local to global. Python variable scope walks through the full lookup order.
Can an inner class access the outer class?
Not on its own. An inner class object has no hidden link to an instance of the outer class, unlike Java’s inner classes. If it needs one, pass the outer object in:
class Account:
rate = 0.04
def __init__(self, owner, balance):
self.owner, self.balance = owner, balance
class Statement:
def __init__(self, account):
self.account = account # pass the outer object in
def summary(self):
a = self.account
interest = a.balance * Account.rate
return f"{a.owner}: ${a.balance:,.2f}, yearly interest ${interest:,.2f}"
acct = Account("Maria Lopez", 12_500.00)
print(Account.Statement(acct).summary())
try:
print(Account.Statement(acct).owner)
except AttributeError as err:
print("AttributeError:", err)
Output:
Maria Lopez: $12,500.00, yearly interest $500.00
AttributeError: 'Statement' object has no attribute 'owner'

Statement only knows about the account because we handed it in.Class-level values like Account.rate are easy, since you can always name the outer class. Instance data like owner needs the instance.
Nesting an Enum inside a class
This is the nested class I use most. Status codes that only make sense for one class read nicely as Ticket.Status.OPEN:
from enum import Enum
class Ticket:
class Status(Enum):
OPEN = "open"
IN_PROGRESS = "in progress"
CLOSED = "closed"
def __init__(self, title):
self.title = title
self.status = Ticket.Status.OPEN
def close(self):
self.status = Ticket.Status.CLOSED
t = Ticket("Printer jam in Dallas office")
print(t.title, "->", t.status.value)
t.close()
print(t.title, "->", t.status.value)
print([s.name for s in Ticket.Status])
Output:
Printer jam in Dallas office -> open
Printer jam in Dallas office -> closed
['OPEN', 'IN_PROGRESS', 'CLOSED']

Ticket.Status keeps the allowed values right next to the class that uses them.You’ll see the same idea in Django, where models carry a nested Meta class for settings, and in many libraries that group config this way.
Nested classes with dataclasses
Dataclasses nest the same way. Quote the type hint as "Employee.Contact", because the name Employee doesn’t exist yet while its body is still running:
from dataclasses import dataclass, field
@dataclass
class Employee:
@dataclass
class Contact:
email: str
phone: str
name: str
city: str
contact: "Employee.Contact"
skills: list = field(default_factory=list)
e = Employee("Sam Carter", "Raleigh", Employee.Contact("sam@example.com", "919-555-0142"))
print(e)
print(e.contact.email)
Output:
Employee(name='Sam Carter', city='Raleigh', contact=Employee.Contact(email='sam@example.com', phone='919-555-0142'), skills=[])
sam@example.com
For more on these, my Python dataclass guide covers fields, defaults and field(default_factory=...).
When to use a class in a class (and when not to)
| Good fit for a nested class | Better as a separate class |
|---|---|
| Only the outer class uses it | Other modules need it too |
| Small helper types, enums, config | Large classes with lots of logic |
You want Outer.Inner naming | You’d need to pass the outer object everywhere |
| Stays short and readable | Indentation gets three levels deep |
My rule of thumb: if the inner class grows past a screen, move it to module level. If you’re really modeling “has a”, look at composition vs inheritance instead.
Here’s where I’d go next with Python classes:
- Constructor in Python
- Python classmethod
- Abstract base classes in Python
- isinstance and issubclass examples
- Constants in Python
Frequently asked questions
Can you have a class inside a class in Python?
Yes. Define it in the outer class body and use it as Outer.Inner. Python calls it a nested or inner class.
How do I create an object of an inner class?
Call it through the outer class: Outer.Inner(args). Inside the outer class’s methods, self.Inner(args) works too.
Why can’t my method see the inner class?
Methods don’t look up names in the class body, so a bare Inner is a NameError. Prefix it with self. or the outer class name.
Can an inner class access outer class variables?
Class variables, yes, by writing Outer.name. Instance variables only if you pass an outer instance to the inner class.
Is a nested class private in Python?
No. Anyone can use Outer.Inner. Nesting is about organizing names, not hiding them.
Are nested classes Pythonic?
They’re fine for small helpers like enums. The Python classes tutorial explains the scoping rules behind them.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn