Skip to content

Learning-DunderMethods is a repository that demystifies Python's magic (dunder) methods. Explore interactive examples and concise explanations on customizing object creation, operator overloading, string representations, and comparisons to write more expressive, Pythonic code.

Notifications You must be signed in to change notification settings

Prime-Hritu/Learning-DunderMethods

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python Magic Methods Examples

This repository contains a collection of self-contained examples demonstrating the use of Python's magic (or "dunder") methods. These methods allow you to customize the behavior of your classes so that your objects work seamlessly with Python’s built-in functions, operators, and other language features.

Table of Contents

Introduction

Python magic methods are special methods with names that begin and end with double underscores (e.g., __init__, __str__, __add__). They enable you to override and extend the behavior of your classes—making your objects support operations such as arithmetic, type conversion, comparison, string representation, and more. This repository provides clear, runnable examples for many categories of magic methods.

Requirements

  • Python 3.6 or higher (Python 3.8+ recommended)
  • Basic familiarity with Python classes and object-oriented programming

Overview of Magic Methods

This repository is organized into several sections. Each section includes example code that demonstrates how to implement and use a set of related magic methods.

Initialization and Construction

These methods control the creation, initialization, and destruction of objects.

  • __new__(cls, ...)
    Called first to create a new instance.
  • __init__(self, ...)
    Initializes the instance after creation.
  • __del__(self)
    Called when the object is about to be destroyed.

Example:

class MyClass:
    def __new__(cls, value):
        print("Calling __new__")
        instance = super().__new__(cls)
        return instance

    def __init__(self, value):
        print("Calling __init__")
        self.value = value

    def __del__(self):
        print(f"Deleting instance with value: {self.value}")

# Create and delete an instance:
obj = MyClass(10)
print("Object value:", obj.value)
del obj

Numeric Magic Methods

These methods let your objects interact with numeric functions and operations (e.g., abs(), round(), bitwise inversion).

  • __trunc__(self): Called by math.trunc()
  • __ceil__(self): Called by math.ceil()
  • __floor__(self): Called by math.floor()
  • __round__(self, n): Called by round()
  • __invert__(self): Called by the ~ operator
  • __abs__(self): Called by abs()
  • __neg__(self): Called by the unary - operator
  • __pos__(self): Called by the unary + operator

Example:

import math

class Number:
    def __init__(self, value):
        self.value = value

    def __trunc__(self):
        print("Calling __trunc__")
        return int(self.value)

    def __ceil__(self):
        print("Calling __ceil__")
        return math.ceil(self.value)

    def __floor__(self):
        print("Calling __floor__")
        return math.floor(self.value)

    def __round__(self, n=0):
        print("Calling __round__")
        return round(self.value, n)

    def __invert__(self):
        print("Calling __invert__")
        return ~int(self.value)

    def __abs__(self):
        print("Calling __abs__")
        return abs(self.value)

    def __neg__(self):
        print("Calling __neg__")
        return -self.value

    def __pos__(self):
        print("Calling __pos__")
        return +self.value

n = Number(3.7)
print("trunc:", math.trunc(n))
print("ceil:", n.__ceil__())
print("floor:", n.__floor__())
print("round (1 decimal):", n.__round__(1))
print("invert (bitwise):", n.__invert__())
print("abs:", abs(n))
print("negated:", -n)
print("unary plus:", +n)

Arithmetic Operators

These methods allow you to override standard arithmetic operations (e.g., +, -, *, /).

  • __add__(self, other): Addition (+)
  • __sub__(self, other): Subtraction (-)
  • __mul__(self, other): Multiplication (*)
  • __floordiv__(self, other): Floor division (//)
  • __truediv__(self, other): True division (/)
  • __mod__(self, other): Modulus (%)
  • __divmod__(self, other): Division and modulus via divmod()
  • __pow__(self, power): Exponentiation (**)
  • __lshift__(self, other): Left bitwise shift (<<)
  • __rshift__(self, other): Right bitwise shift (>>)
  • __and__(self, other): Bitwise AND (&)
  • __or__(self, other): Bitwise OR (|)
  • __xor__(self, other): Bitwise XOR (^)

Example:

class Vector:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x + other.x, self.y + other.y)
        raise TypeError("Unsupported operand type(s) for +")

    def __sub__(self, other):
        if isinstance(other, Vector):
            return Vector(self.x - other.x, self.y - other.y)
        raise TypeError("Unsupported operand type(s) for -")

    def __mul__(self, other):
        if isinstance(other, (int, float)):
            return Vector(self.x * other, self.y * other)
        elif isinstance(other, Vector):
            # Dot product
            return self.x * other.x + self.y * other.y
        raise TypeError("Unsupported operand type(s) for *")

    # (Other methods omitted for brevity)

    def __repr__(self):
        return f"Vector({self.x}, {self.y})"

v1 = Vector(3, 4)
v2 = Vector(1, 2)
print("v1 + v2 =", v1 + v2)
print("v1 - v2 =", v1 - v2)
print("v1 * 3 =", v1 * 3)
print("v1 * v2 (dot product) =", v1 * v2)

String Magic Methods

Customize how objects are converted to strings, hashed, and represented.

  • __str__(self): Called by str() and print() for a user-friendly representation.
  • __repr__(self): Called by repr() for a developer-friendly representation.
  • __unicode__(self): (Mostly relevant in Python 2; in Python 3, __str__ returns a Unicode string.)
  • __format__(self, format_spec): Called by the format() function.
  • __hash__(self): Called by hash()
  • __bool__(self): Called by bool() (renamed from __nonzero__ in Python 3)
  • __dir__(self): Returns a list of attributes (used by dir())
  • __sizeof__(self): Returns the size in bytes (used by sys.getsizeof())

Example:

class CustomString:
    def __init__(self, text):
        self.text = text

    def __str__(self):
        return f"CustomString: {self.text}"

    def __repr__(self):
        return f"CustomString({self.text!r})"

    def __unicode__(self):
        return self.text

    def __format__(self, format_spec):
        return format(self.text, format_spec)

    def __hash__(self):
        return hash(self.text)

    def __bool__(self):
        return bool(self.text)

    def __dir__(self):
        return ['text']

    def __sizeof__(self):
        return len(self.text)

cs = CustomString("Hello, World!")
print("str:", str(cs))
print("repr:", repr(cs))
print("Formatted:", format(cs, "^20"))
print("Hash:", hash(cs))
print("Bool:", bool(cs))
print("dir:", dir(cs))
print("Size (approx):", cs.__sizeof__(), "bytes")

Comparison Magic Methods

These methods let you compare objects using operators such as ==, <, >, <=, and >=.

  • __eq__(self, other): Equality (==)
  • __ne__(self, other): Inequality (!=)
  • __lt__(self, other): Less than (<)
  • __gt__(self, other): Greater than (>)
  • __le__(self, other): Less than or equal to (<=)
  • __ge__(self, other): Greater than or equal to (>=)

Example:

class Comparable:
    def __init__(self, value):
        self.value = value

    def __eq__(self, other):
        if isinstance(other, Comparable):
            return self.value == other.value
        return NotImplemented

    def __ne__(self, other):
        if isinstance(other, Comparable):
            return self.value != other.value
        return NotImplemented

    def __lt__(self, other):
        if isinstance(other, Comparable):
            return self.value < other.value
        return NotImplemented

    def __gt__(self, other):
        if isinstance(other, Comparable):
            return self.value > other.value
        return NotImplemented

    def __le__(self, other):
        if isinstance(other, Comparable):
            return self.value <= other.value
        return NotImplemented

    def __ge__(self, other):
        if isinstance(other, Comparable):
            return self.value >= other.value
        return NotImplemented

    def __repr__(self):
        return f"Comparable({self.value})"

a = Comparable(10)
b = Comparable(20)
print("a == b:", a == b)
print("a != b:", a != b)
print("a < b :", a < b)
print("a > b :", a > b)
print("a <= b:", a <= b)
print("a >= b:", a >= b)

Usage

  1. Clone the Repository

    git clone https://github.com/yourusername/python-magic-methods.git
    cd python-magic-methods
  2. Run an Example
    Each example is self-contained. For instance, to run the initialization example:

    python init_example.py

    Alternatively, you can copy any code snippet into a new Python file and run it with:

    python your_example.py
  3. Experiment and Modify
    Feel free to modify the examples or combine them in your projects to better understand how magic methods integrate with Python’s operations.

Further Reading

License

This project is provided for educational purposes. Feel free to use, modify, and distribute the code under the terms of the MIT License.


Happy Pythoning!

About

Learning-DunderMethods is a repository that demystifies Python's magic (dunder) methods. Explore interactive examples and concise explanations on customizing object creation, operator overloading, string representations, and comparisons to write more expressive, Pythonic code.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages