EASYTUTORGUIDE

Practical tutorials, tools, courses, digital skills, and business promotion.

Free Learning

JavaScript – Chapter 2: Development Environment

Learn what JavaScript is, where it runs, why it is important, and how beginners can start using it with simple examples and clear output.

Beginner Friendly JavaScript Basics Web Development Code Examples
JavaScript Lesson 2 Chapter 2 Topics Day / Night Mode

Main reading content

python-course-chapter-19

Chapter 19: Magic Methods and Python Data Model

Learn how special methods let custom objects behave like built-in Python values.

Goal: Understand Python magic methods and use them to create readable, useful, and Python-like classes.

Chapter 19 Topics

19.1 Introduction to Magic Methods

Magic methods are special methods whose names begin and end with two underscores. Python calls these methods automatically when an object is printed, compared, added, indexed, measured, or used in another built-in operation. Beginners usually do not call magic methods directly. Instead, they define them inside classes so their objects behave naturally.

Example

class Greeting:
    def __str__(self):
        return "Hello from a custom object"

message = Greeting()
print(message)

Output

Hello from a custom object

Output explanation: The print() function asks the object for a readable string. Python automatically calls __str__(), which returns the displayed sentence.

19.2 The Python Data Model

The Python data model is the collection of rules that explains how Python objects interact with the language. It defines what happens when you call functions such as len(), use operators such as +, access an item with brackets, or loop through an object. Magic methods connect custom classes to these standard Python operations.

Example

class Team:
    def __init__(self, members):
        self.members = members

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

team = Team(["Ali", "Sara", "Mina"])
print(len(team))

Output

3

Output explanation: Calling len(team) activates the class's __len__() method. That method returns the number of names stored in the internal list.

19.3 __new__()

The __new__() method creates a new object before __init__() prepares it. It receives the class as its first argument and must return a new instance. Most beginner classes do not need to define __new__(), because Python already handles object creation. It is mainly useful for immutable types or advanced creation control.

Example

class Student:
    def __new__(cls):
        print("Creating the object")
        return super().__new__(cls)

    def __init__(self):
        print("Initializing the object")

student = Student()

Output

Creating the object
Initializing the object

Output explanation: Python first calls __new__() to create the instance. After the instance exists, Python calls __init__() to prepare its starting data.

19.4 __init__()

The __init__() method initializes a newly created object. It is often called the constructor by beginners, although object creation technically happens in __new__(). You use __init__() to store starting values in instance attributes. Python runs it automatically whenever you create an object by calling the class name.

Example

class Book:
    def __init__(self, title, price):
        self.title = title
        self.price = price

book = Book("Python Basics", 25)
print(book.title)
print(book.price)

Output

Python Basics
25

Output explanation: The values passed to Book() are stored in the new object's title and price attributes, and the two print statements display them.

19.5 __del__()

The __del__() method may run when an object is about to be destroyed. It can be used for simple cleanup messages, but it should not be trusted for important tasks because the exact destruction time is not always predictable. Safer cleanup is usually handled with context managers, especially for files, connections, and other resources.

Example

class Demo:
    def __del__(self):
        print("Object removed")

item = Demo()
del item

Output

Object removed

Output explanation: The del statement removes the variable reference. In this simple example, Python destroys the object and calls __del__(), which prints the message.

19.6 __str__()

The __str__() method returns a friendly description of an object for users. Python calls it when an object is passed to print() or str(). Without this method, printing a custom object usually shows a less helpful technical value. A good __str__() result should be clear, readable, and easy to understand.

Example

class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def __str__(self):
        return f"{self.name}: ${self.price}"

product = Product("Keyboard", 40)
print(product)

Output

Keyboard: $40

Output explanation: The object is printed directly. Python calls __str__(), which builds and returns a readable product description.

19.7 __repr__()

The __repr__() method returns a detailed representation mainly intended for programmers and debugging. A useful representation often shows the class name and the values needed to understand or recreate the object. Python uses it in the interactive console and when objects appear inside collections. It also becomes a fallback when __str__() is missing.

Example

class User:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def __repr__(self):
        return f"User(name={self.name!r}, age={self.age})"

user = User("Lina", 20)
print(repr(user))

Output

User(name='Lina', age=20)

Output explanation: The repr() function calls __repr__(). The returned text clearly shows the class and its stored values.

19.8 __len__()

The __len__() method lets a custom object work with Python's len() function. It must return a non-negative integer. The meaning of length depends on the class. For a playlist it may mean the number of songs, while for a shopping cart it may mean the number of products currently stored.

Example

class Playlist:
    def __init__(self, songs):
        self.songs = songs

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

playlist = Playlist(["Song A", "Song B", "Song C"])
print(len(playlist))

Output

3

Output explanation: The playlist contains three strings. The len() function calls the object's __len__() method and receives the number 3.

19.9 __getitem__()

The __getitem__() method controls what happens when square brackets are used to read an item from an object. The value inside the brackets is passed as the key or index. This method can make a custom class behave like a list, tuple, or dictionary. It can also support slicing when slice objects are handled correctly.

Example

class Menu:
    def __init__(self, items):
        self.items = items

    def __getitem__(self, index):
        return self.items[index]

menu = Menu(["Pizza", "Pasta", "Salad"])
print(menu[1])

Output

Pasta

Output explanation: The expression menu[1] calls __getitem__(1). The internal list item at index 1 is Pasta.

19.10 __setitem__()

The __setitem__() method controls assignments made with square brackets. Python calls it when code such as object[key] = value is used. This lets a custom object support list-like or dictionary-like updates. The method can directly store the value or validate it before changing the object's internal data.

Example

class Scores:
    def __init__(self):
        self.data = {}

    def __setitem__(self, name, score):
        self.data[name] = score

scores = Scores()
scores["Ali"] = 95
print(scores.data)

Output

{'Ali': 95}

Output explanation: The bracket assignment calls __setitem__(). It stores the key Ali and value 95 in the internal dictionary.

19.11 __delitem__()

The __delitem__() method runs when an item is deleted with bracket syntax, such as del object[key]. It gives the class control over how stored values are removed. A class can delete the item immediately, prevent deletion, print a message, or raise a helpful error when the requested key does not exist.

Example

class Basket:
    def __init__(self):
        self.items = ["Apple", "Bread", "Milk"]

    def __delitem__(self, index):
        del self.items[index]

basket = Basket()
del basket[1]
print(basket.items)

Output

['Apple', 'Milk']

Output explanation: The statement del basket[1] calls __delitem__(1). The item at index 1, which is Bread, is removed.

19.12 __contains__()

The __contains__() method defines how the in and not in operators check membership in a custom object. It should return True when the value exists and False otherwise. This method can search an internal collection or apply a custom rule, such as checking usernames without case sensitivity.

Example

class Library:
    def __init__(self, books):
        self.books = books

    def __contains__(self, title):
        return title in self.books

library = Library(["Python Basics", "Web Design"])
print("Python Basics" in library)
print("Java" in library)

Output

True
False

Output explanation: Python calls __contains__() for each membership test. The first title exists in the list, while the second title does not.

19.13 __iter__()

The __iter__() method makes an object iterable, meaning it can be used in a for loop. It must return an iterator. A simple class that already stores a list can return iter(self.items). More advanced classes may return themselves and then provide a separate __next__() method to produce values one at a time.

Example

class Colors:
    def __init__(self):
        self.items = ["Red", "Green", "Blue"]

    def __iter__(self):
        return iter(self.items)

colors = Colors()
for color in colors:
    print(color)

Output

Red
Green
Blue

Output explanation: The loop calls __iter__(), which returns an iterator for the internal list. The loop then prints each color in order.

19.14 __next__()

The __next__() method returns the next value from an iterator. When no values remain, it must raise StopIteration. Python automatically handles that exception inside a for loop and stops looping. A custom iterator usually stores its current position as an attribute and increases that position after returning each value.

Example

class Counter:
    def __init__(self, limit):
        self.current = 1
        self.limit = limit

    def __iter__(self):
        return self

    def __next__(self):
        if self.current > self.limit:
            raise StopIteration
        value = self.current
        self.current += 1
        return value

for number in Counter(3):
    print(number)

Output

1
2
3

Output explanation: Each loop cycle calls __next__(). The method returns 1, 2, and 3, then raises StopIteration to end the loop.

19.15 Comparison Magic Methods

Comparison magic methods define how custom objects react to operators such as ==, !=, <, <=, >, and >=. Common methods include __eq__(), __lt__(), and __gt__(). They usually compare one important attribute, but they should first confirm that the other value is a compatible object.

Example

class Player:
    def __init__(self, score):
        self.score = score

    def __gt__(self, other):
        return self.score > other.score

player1 = Player(90)
player2 = Player(75)
print(player1 > player2)

Output

True

Output explanation: The > operator calls player1.__gt__(player2). Since 90 is greater than 75, the method returns True.

19.16 Arithmetic Magic Methods

Arithmetic magic methods let custom objects work with mathematical operators. Examples include __add__() for addition, __sub__() for subtraction, __mul__() for multiplication, and __truediv__() for division. These methods usually return a new object rather than changing the existing object, which makes their behavior similar to numbers and other built-in values.

Example

class Money:
    def __init__(self, amount):
        self.amount = amount

    def __add__(self, other):
        return Money(self.amount + other.amount)

    def __str__(self):
        return f"${self.amount}"

first = Money(20)
second = Money(35)
print(first + second)

Output

$55

Output explanation: The + operator calls __add__(), which creates a new Money object containing 55. Printing that object calls __str__().

19.17 Callable Objects with __call__()

The __call__() method lets an object be used like a function. After defining it, you can place parentheses after an object and optionally pass arguments. Callable objects are useful when an object needs to remember information between calls. They combine stored state from a class with the convenient calling style of a normal function.

Example

class Multiplier:
    def __init__(self, factor):
        self.factor = factor

    def __call__(self, number):
        return number * self.factor

double = Multiplier(2)
print(double(6))

Output

12

Output explanation: The expression double(6) calls the object's __call__() method. It multiplies 6 by the stored factor 2.

19.18 Context Manager Methods

Context managers control setup and cleanup around a block of code used with the with statement. The __enter__() method runs when the block begins, and __exit__() runs when the block ends, even if an error occurs. They are commonly used for files, database connections, locks, and other resources that must be cleaned up safely.

Example

class StudySession:
    def __enter__(self):
        print("Session started")
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        print("Session ended")

with StudySession():
    print("Learning Python")

Output

Session started
Learning Python
Session ended

Output explanation: Entering the with block calls __enter__(). After the body runs, Python calls __exit__() to complete cleanup.

19.19 Attribute Access Methods

Attribute access methods control how object attributes are read, assigned, or deleted. Important methods include __getattr__(), __getattribute__(), __setattr__(), and __delattr__(). These methods are powerful and should be used carefully because mistakes can cause endless recursion. A common beginner use is returning a helpful value when a missing attribute is requested.

Example

class Profile:
    def __init__(self, name):
        self.name = name

    def __getattr__(self, attribute):
        return f"{attribute} is not available"

profile = Profile("Sara")
print(profile.name)
print(profile.phone)

Output

Sara
phone is not available

Output explanation: The existing name attribute is returned normally. Because phone does not exist, Python calls __getattr__() and displays its message.

19.20 Creating Custom Python-Like Objects

A Python-like object supports familiar operations that match its purpose. For example, a custom collection may provide length, indexing, membership testing, iteration, and a readable string representation. You should not add magic methods only because they exist. Choose methods that make the class intuitive, predictable, and similar to related built-in Python objects.

Example

class TaskList:
    def __init__(self, tasks):
        self.tasks = tasks

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

    def __getitem__(self, index):
        return self.tasks[index]

    def __contains__(self, task):
        return task in self.tasks

    def __str__(self):
        return ", ".join(self.tasks)

tasks = TaskList(["Study", "Exercise"])
print(len(tasks))
print(tasks[0])
print("Exercise" in tasks)
print(tasks)

Output

2
Study
True
Study, Exercise

Output explanation: Different operations call different magic methods. The object supports length checking, indexing, membership testing, and readable printing like a built-in collection.

19.21 Chapter Practice Exercises

These exercises help you practise the most important magic methods from this chapter. Complete each task in a separate Python file, run it, and compare the result with what you expected. Try writing the solution yourself before reviewing earlier examples. Practice is important because magic methods become easier when you connect each method to a normal Python operation.

Example Exercise Solution

class Classroom:
    def __init__(self, students):
        self.students = students

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

    def __contains__(self, name):
        return name in self.students

classroom = Classroom(["Ali", "Mina", "Sara"])
print(len(classroom))
print("Mina" in classroom)

Output

3
True

Output explanation: The class contains three names, so len(classroom) returns 3. The membership test returns True because Mina is stored in the list.

Practice Tasks

  1. Create a Movie class with readable __str__() and __repr__() methods.
  2. Create a NumberBox class that supports addition with __add__().
  3. Create a collection class that supports len(), indexing, and membership testing.
  4. Create an iterator that returns even numbers from 2 to 10.
  5. Create a callable object that adds a stored amount to a supplied number.

19.22 Chapter Mini Project

In this mini project, you will build a small shopping cart that behaves like a Python collection. It will support adding products, checking the number of products, reading products by index, checking whether a product exists, looping through products, and displaying a friendly summary. This project combines several magic methods in one practical class.

Mini Project: Python-Like Shopping Cart

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, item):
        self.items.append(item)

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

    def __getitem__(self, index):
        return self.items[index]

    def __contains__(self, item):
        return item in self.items

    def __iter__(self):
        return iter(self.items)

    def __str__(self):
        if not self.items:
            return "The cart is empty."
        return "Cart: " + ", ".join(self.items)

cart = ShoppingCart()
cart.add("Bread")
cart.add("Milk")
cart.add("Apples")

print(cart)
print("Number of items:", len(cart))
print("First item:", cart[0])
print("Milk" in cart)

for item in cart:
    print("-", item)

Output

Cart: Bread, Milk, Apples
Number of items: 3
First item: Bread
True
- Bread
- Milk
- Apples

Output explanation: The cart uses __str__() for its summary, __len__() for the item count, __getitem__() for indexing, __contains__() for the membership test, and __iter__() for the loop. Together, these methods make the custom cart feel like a normal Python collection.

Chapter 2: Development EnvironmentPrepare Your JavaScript Workspace

A modern course built to help learners study step by step with clarity, comfort, and confidence.