Python Inner Classes
Inner Classes
An inner class (also called a nested class) is a class defined inside another class. It is used when a class is closely tied to another class and has little meaning on its own — for example, a Battery that only exists as part of a Laptop, or Settings that belong to a Profile.
Inner classes help you:
- Organize related code — the helper class lives right next to the class that uses it
- Improve readability — the structure of the code mirrors the structure of the real-world object
- Limit the scope of helper classes — the inner class is accessed through the outer class, so it does not clutter the module's top-level names
Defining an Inner Class
An inner class is declared inside the outer class's body, at the same indentation level as its methods.
Example: A Basic Inner Class
class Container:
def __init__(self):
self.title = "Main Container"
class Item:
def show(self):
print("This is an item inside the container")
box = Container()
print(box.title)
Expected output:
Main Container
Containeris the outer class.Itemis the inner class, defined insideContainer's body.- Creating a
Containerobject does not automatically create anItemobject. The inner class is only a definition until you create an instance of it.
Creating Objects of an Inner Class
An inner class is an attribute of the outer class, so you reach it with dot notation. There are two ways to access it.
Through an Instance of the Outer Class
class Library:
def __init__(self):
self.name = "City Library"
class Book:
def info(self):
print("Book belongs to the library")
library = Library()
book = library.Book()
book.info()
Expected output:
Book belongs to the library
library.Book looks up the Book class through the library object, and the parentheses create a new Book object.
Through the Outer Class Directly
You do not need an outer object to create an inner object. You can access the inner class through the outer class name:
book = Library.Book()
book.info()
Expected output:
Book belongs to the library
Both forms create the same kind of object, because the inner class belongs to the outer class, not to any particular outer object.
Accessing Outer Class Data From an Inner Class
Unlike in some other languages (such as Java), a Python inner class object has no automatic connection to an outer class object. Being defined inside another class only affects where the class is found, not what its objects can access.
To use the outer object's data, pass the outer object explicitly to the inner class.
Example: Passing the Outer Object
class Profile:
def __init__(self):
self.username = "guest_user"
class Settings:
def __init__(self, profile):
self.profile = profile
def display(self):
print("Profile username:", self.profile.username)
profile = Profile()
settings = profile.Settings(profile)
settings.display()
Expected output:
Profile username: guest_user
How it works:
profileis created withusername = "guest_user".profile.Settings(profile)creates aSettingsobject and passes theprofileobject to its__init__().- The
Settingsobject stores that reference inself.profile. display()readsself.profile.username— the outer object's data.
Without the explicit reference, Settings would have no way to know which Profile it belongs to.
Practical Use Case: Modeling Components
Inner classes are useful for modeling components that belong to a larger object. Often, the outer class creates its inner objects in its own __init__().
Example: A Laptop With a Battery
class Laptop:
def __init__(self, brand):
self.brand = brand
self.battery = self.Battery()
class Battery:
def __init__(self):
self.level = 0
def charge(self):
self.level = 100
print("Battery fully charged")
def drain(self):
self.level = 10
print("Battery low")
def use(self):
if self.battery.level > 20:
print(f"Using the {self.brand} laptop")
else:
print("Please charge the battery first")
lap = Laptop("Dell")
lap.use()
lap.battery.charge()
lap.use()
Expected output:
Please charge the battery first
Battery fully charged
Using the Dell laptop
How it works:
Laptop("Dell")runsLaptop.__init__(), which stores the brand and creates aBatteryobject withself.Battery(). The new battery starts at level0.- The first
lap.use()checksself.battery.level. Because0is not greater than20, it asks for a charge. lap.battery.charge()calls theBatteryobject's method, setting its level to100.- The second
lap.use()now sees a level of100and uses the laptop.
Each Laptop object gets its own Battery object, so charging one laptop does not affect another. This kind of relationship — a laptop has a battery — is called composition.
Using Multiple Inner Classes
An outer class can contain several inner classes, each representing a different component.
Example: A Smartphone With a Camera and a Speaker
class Smartphone:
def __init__(self):
self.camera = self.Camera()
self.speaker = self.Speaker()
class Camera:
def capture(self):
print("Photo captured")
class Speaker:
def play_sound(self):
print("Playing sound")
phone = Smartphone()
phone.camera.capture()
phone.speaker.play_sound()
Expected output:
Photo captured
Playing sound
phone.camera and phone.speaker are separate objects created during the smartphone's initialization. Each component handles its own responsibility, and the Smartphone class brings them together.
When to Use Inner Classes
Inner classes are a good fit when:
- The inner class is only meaningful as part of the outer class.
- You want to group a small helper class with the class that uses it.
- You want to signal to readers that the classes belong together.
Prefer separate, top-level classes when:
- The class is useful on its own or is used by several other classes.
- The inner class is large or complex.
- Other modules need to import and use the class directly.
Inner classes are used less often in Python than in some other languages. Many Python developers prefer separate classes combined through composition (self.battery = Battery()), which is equally clear and easier to reuse.
Common Mistakes
| Mistake | Problem |
|---|---|
| Expecting the inner object to access outer attributes automatically | AttributeError; pass the outer object explicitly |
| Assuming creating the outer object creates inner objects | Inner objects are created only when you call the inner class |
Calling Battery() inside the outer class without self. or the class name | NameError; use self.Battery() or Laptop.Battery() |
| Nesting classes deeply | Makes code hard to read and test |
Related Concepts
- Python Classes and Objects — defining and instantiating classes
- Python
__init__()Method — creating component objects during initialization - Python Inheritance — the "is-a" relationship, in contrast to composition's "has-a"
- Python Encapsulation — grouping related data and behavior