Last modified: Feb 10, 2026 By Alexander Williams
Python Objects in Objects: Nested Data Guide
Python is an object-oriented language. Everything is an object. This includes integers, strings, and lists. You can also create your own objects using classes.
Often, you need to model complex data. A single object might not be enough. This is where the concept of an object inside another object becomes powerful. It is also called object composition or nesting.
This article explains this concept clearly. You will learn how to create nested objects. You will also learn how to access and manage their data.
What Are Objects in Python?
Before diving into nesting, let's review basics. An object is an instance of a class. A class is a blueprint. It defines attributes and methods.
Attributes are data. Methods are functions that act on that data. For a deeper dive, see our guide on Python Objects: Classes, Instances, and Methods Guide.
Creating an object is simple. You define a class and then instantiate it.
# Defining a simple class
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
# Creating an object (instance)
my_book = Book("Python 101", "Jane Doe")
print(my_book.title)
Python 101
Why Put an Object Inside Another Object?
Real-world data has relationships. A library has many books. A car has an engine. A university has departments with students.
Modeling these relationships is easier with nested objects. It keeps your code organized. It mirrors the real-world structure.
This approach is called composition. It is a core principle of object-oriented design. You build complex objects from simpler ones.
Creating an Object Inside an Object
Let's build a practical example. We will create a Library class. It will contain a list of Book objects.
The Book object becomes an attribute of the Library object. This is object nesting.
class Book:
def __init__(self, title, author, isbn):
self.title = title
self.author = author
self.isbn = isbn
def get_info(self):
return f"'{self.title}' by {self.author}"
class Library:
def __init__(self, name):
self.name = name
self.books = [] # This list will hold Book objects
def add_book(self, book_object):
"""Adds a Book object to the library's collection."""
self.books.append(book_object)
print(f"Added: {book_object.get_info()}")
def list_books(self):
"""Prints all books in the library."""
print(f"\n--- Books in {self.name} ---")
for book in self.books:
print(f" - {book.get_info()}")
Now, let's use these classes. We create Book objects. Then we add them to a Library object.
# Create some Book objects
book1 = Book("Fluent Python", "Luciano Ramalho", "12345")
book2 = Book("Automate the Boring Stuff", "Al Sweigart", "67890")
# Create a Library object
my_library = Library("City Central Library")
# Add Book objects to the Library object
my_library.add_book(book1)
my_library.add_book(book2)
# List all books
my_library.list_books()
Added: 'Fluent Python' by Luciano Ramalho
Added: 'Automate the Boring Stuff' by Al Sweigart
--- Books in City Central Library ---
- 'Fluent Python' by Luciano Ramalho
- 'Automate the Boring Stuff' by Al Sweigart
The Library.books attribute is a list. It contains other objects. This is a common and powerful pattern.
Accessing Nested Object Data
To access data inside a nested object, you chain attribute access. Use the dot notation multiple times.
Be careful. You must ensure the inner object exists. Otherwise, you might get an AttributeError.
# Accessing nested attributes
print(my_library.books[0].title) # Access title of the first book
print(my_library.books[1].author) # Access author of the second book
# Looping through nested objects
print("\nAll titles:")
for book in my_library.books:
print(book.title)
Fluent Python
Al Sweigart
All titles:
Fluent Python
Automate the Boring Stuff
A More Complex Example: Multiple Levels
Objects can be nested many levels deep. Consider a university system. A University has Department objects. A Department has Student objects.
class Student:
def __init__(self, name, student_id):
self.name = name
self.student_id = student_id
class Department:
def __init__(self, dept_name):
self.dept_name = dept_name
self.students = []
def enroll_student(self, student):
self.students.append(student)
class University:
def __init__(self, uni_name):
self.uni_name = uni_name
self.departments = []
def add_department(self, department):
self.departments.append(department)
# Creating the structure
cs_dept = Department("Computer Science")
math_dept = Department("Mathematics")
alice = Student("Alice Johnson", "S001")
bob = Student("Bob Smith", "S002")
cs_dept.enroll_student(alice)
math_dept.enroll_student(bob)
my_uni = University("Tech University")
my_uni.add_department(cs_dept)
my_uni.add_department(math_dept)
# Accessing deeply nested data
print(f"Student in CS: {my_uni.departments[0].students[0].name}")
print(f"Department: {my_uni.departments[1].dept_name}")
Student in CS: Alice Johnson
Department: Mathematics
This shows a three-level nested structure. It is clear and logical.
Serializing Nested Objects to JSON
Often, you need to save or send nested objects. JSON is a common format. Python's json module can handle this.
However, custom objects are not JSON-serializable by default. You need to convert them to dictionaries first. Our JSON Serialization Guide for Custom Python Objects covers this in detail.
Here is a quick example using a custom method.
import json
class Book:
def __init__(self, title, author):
self.title = title
self.author = author
def to_dict(self):
"""Converts the object to a dictionary for JSON serialization."""
return {"title": self.title, "author": self.author}
class Library:
def __init__(self, name):
self.name = name
self.books = []
def add_book(self, book):
self.books.append(book)
def to_dict(self):
"""Converts the entire library, including nested books, to a dict."""
return {
"library_name": self.name,
"books": [book.to_dict() for book in self.books]
}
# Create structure
lib = Library("My Lib")
lib.add_book(Book("Book One", "Author A"))
lib.add_book(Book("Book Two", "Author B"))
# Convert to JSON string
library_dict = lib.to_dict()
json_string = json.dumps(library_dict, indent=2)
print(json_string)
{
"library_name": "My Lib",
"books": [
{
"title": "Book One",
"author": "Author A"
},
{
"title": "Book Two",
"author": "Author B"
}
]
}
To convert JSON back into objects, you can use json.loads(). Learn more in our article on Python JSON loads: Converting JSON to Python Objects.
Best Practices and Common Pitfalls
Using nested objects is powerful. But you must follow good practices.
Keep Relationships Clear: Ensure the nesting makes logical sense. A Car having an Engine object is good. A Car having a Book object might not be.
Avoid Circular References: Do not let Object A contain Object B, and Object B also contain Object A. This can cause problems with memory and serialization.
Check for None: Always check if an inner object exists before accessing its attributes. Use conditional statements.
# Safe access check
if my_library.books: # Check if the list is not empty
first_book_title = my_library.books[0].title
print(first_book_title)
else:
print("No books in the library.")
Conclusion
Putting objects inside other objects is a fundamental technique. It is called composition. It helps you model complex, real-world data structures in Python.
You learned how to create nested objects. You saw how to access their data through chained attributes. We also covered serialization to JSON.
Start with simple examples like the Library and Book. Then move to more complex structures. Remember to keep relationships logical and check for empty references.
This approach will make your code more organized. It will be easier to read, maintain, and extend. Mastering object composition is a key step in becoming a proficient Python programmer.