<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ python magic method - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ python magic method - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 26 Jul 2026 04:13:46 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/python-magic-method/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How Python Magic Methods Work: A Practical Guide ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wondered how Python makes objects work with operators like + or -? Or how it knows how to display objects when you print them? The answer lies in Python's magic methods, also known as dunder (double under) methods. Magic methods are spe... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/python-magic-methods-practical-guide/</link>
                <guid isPermaLink="false">67dc33ff64b6723cee52486b</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ python magic method ]]>
                    </category>
                
                    <category>
                        <![CDATA[ dunder method ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Tutorial ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python advanced ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vivek Sahu ]]>
                </dc:creator>
                <pubDate>Thu, 20 Mar 2025 15:27:59 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742482738702/0b357de2-855d-47c2-960f-453e0bfd9a3d.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wondered how Python makes objects work with operators like <code>+</code> or <code>-</code>? Or how it knows how to display objects when you print them? The answer lies in Python's magic methods, also known as dunder (d<s>ouble</s> under) methods.</p>
<p>Magic methods are special methods that let you define how your objects behave in response to various operations and built-in functions. They're what makes Python's object-oriented programming so powerful and intuitive.</p>
<p>In this guide, you'll learn how to use magic methods to create more elegant and powerful code. You'll see practical examples that show how these methods work in real-world scenarios.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Basic understanding of Python syntax and object-oriented programming concepts.</p>
</li>
<li><p>Familiarity with classes, objects, and inheritance.</p>
</li>
<li><p>Knowledge of built-in Python data types (lists, dictionaries, and so on).</p>
</li>
<li><p>A working Python 3 installation is recommended to actively engage with the examples here.</p>
</li>
</ul>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<ol>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#what-are-magic-methods">What are Magic Methods?</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#object-representation">Object Representation</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#str-vs-repr"><strong>str</strong> vs <strong>repr</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-custom-error-class">Practical Example: Custom Error Class</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#operator-overloading">Operator Overloading</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#arithmetic-operators">Arithmetic Operators</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#comparison-operators">Comparison Operators</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-money-class">Practical Example: Money Class</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#container-methods">Container Methods</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#sequence-protocol">Sequence Protocol</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#mapping-protocol">Mapping Protocol</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-custom-cache">Practical Example: Custom Cache</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#attribute-access">Attribute Access</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#getattr-and-getattribute"><strong>getattr</strong> and <strong>getattribute</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#setattr-and-delattr"><strong>setattr</strong> and <strong>delattr</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-auto-logging-properties">Practical Example: Auto-Logging Properties</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#context-managers">Context Managers</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#enter-and-exit"><strong>enter</strong> and <strong>exit</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-database-connection-manager">Practical Example: Database Connection Manager</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#callable-objects">Callable Objects</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#call"><strong>call</strong></a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#practical-example-memoization-decorator">Practical Example: Memoization Decorator</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#advanced-magic-methods">Advanced Magic Methods</a></p>
<ul>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#new-for-object-creation"><strong>new</strong> for Object Creation</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#slots-for-memory-optimization"><strong>slots</strong> for Memory Optimization</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#missing-for-default-dictionary-values"><strong>missing</strong> for Default Dictionary Values</a></p>
</li>
</ul>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#performance-considerations">Performance Considerations</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#best-practices">Best Practices</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></p>
</li>
<li><p><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-15-magic-methods-in-python.md#references">References</a></p>
</li>
</ol>
<h2 id="heading-what-are-magic-methods"><strong>What are Magic Methods?</strong></h2>
<p>Magic methods in Python are special methods that start and end with double underscores (<code>__</code>). When you use certain operations or functions on your objects, Python automatically calls these methods.</p>
<p>For example, when you use the <code>+</code> operator on two objects, Python looks for the <code>__add__</code> method in the left operand. If it finds it, it calls that method with the right operand as an argument.</p>
<p>Here's a simple example that shows how this works:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Point</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, x, y</span>):</span>
        self.x = x
        self.y = y

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__add__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">return</span> Point(self.x + other.x, self.y + other.y)

p1 = Point(<span class="hljs-number">1</span>, <span class="hljs-number">2</span>)
p2 = Point(<span class="hljs-number">3</span>, <span class="hljs-number">4</span>)
p3 = p1 + p2  <span class="hljs-comment"># This calls p1.__add__(p2)</span>
print(p3.x, p3.y)  <span class="hljs-comment"># Output: 4 6</span>
</code></pre>
<p>Let's break down what's happening here:</p>
<ol>
<li><p>We create a <code>Point</code> class that represents a point in 2D space</p>
</li>
<li><p>The <code>__init__</code> method initializes the x and y coordinates</p>
</li>
<li><p>The <code>__add__</code> method defines what happens when we add two points</p>
</li>
<li><p>When we write <code>p1 + p2</code>, Python automatically calls <code>p1.__add__(p2)</code></p>
</li>
<li><p>The result is a new <code>Point</code> with coordinates (4, 6)</p>
</li>
</ol>
<p>This is just the beginning. Python has many magic methods that let you customize how your objects behave in different situations. Let's explore some of the most useful ones.</p>
<h2 id="heading-object-representation"><strong>Object Representation</strong></h2>
<p>When you work with objects in Python, you often need to convert them to strings. This happens when you print an object or try to display it in the interactive console. Python provides two magic methods for this purpose: <code>__str__</code> and <code>__repr__</code>.</p>
<h3 id="heading-str-vs-repr"><strong>str vs repr</strong></h3>
<p>The <code>__str__</code> and <code>__repr__</code> methods serve different purposes:</p>
<ul>
<li><p><code>__str__</code>: Called by the <code>str()</code> function and by the <code>print()</code> function. It should return a string that is readable for end-users.</p>
</li>
<li><p><code>__repr__</code>: Called by the <code>repr()</code> function and used in the interactive console. It should return a string that, ideally, could be used to recreate the object.</p>
</li>
</ul>
<p>Here's an example that shows the difference:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Temperature</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, celsius</span>):</span>
        self.celsius = celsius

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{self.celsius}</span>°C"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Temperature(<span class="hljs-subst">{self.celsius}</span>)"</span>

temp = Temperature(<span class="hljs-number">25</span>)
print(str(temp))      <span class="hljs-comment"># Output: 25°C</span>
print(repr(temp))     <span class="hljs-comment"># Output: Temperature(25)</span>
</code></pre>
<p>In this example:</p>
<ul>
<li><p><code>__str__</code> returns a user-friendly string showing the temperature with a degree symbol</p>
</li>
<li><p><code>__repr__</code> returns a string that shows how to create the object, which is useful for debugging</p>
</li>
</ul>
<p>The difference becomes clear when you use these objects in different contexts:</p>
<ul>
<li><p>When you print the temperature, you see the user-friendly version: <code>25°C</code></p>
</li>
<li><p>When you inspect the object in the Python console, you see the detailed version: <code>Temperature(25)</code></p>
</li>
</ul>
<h3 id="heading-practical-example-custom-error-class"><strong>Practical Example: Custom Error Class</strong></h3>
<p>Let's create a custom error class that provides better debugging information. This example shows how you can use <code>__str__</code> and <code>__repr__</code> to make your error messages more helpful:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ValidationError</span>(<span class="hljs-params">Exception</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, field, message, value=None</span>):</span>
        self.field = field
        self.message = message
        self.value = value
        super().__init__(self.message)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> self.value <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"Error in field '<span class="hljs-subst">{self.field}</span>': <span class="hljs-subst">{self.message}</span> (got: <span class="hljs-subst">{repr(self.value)}</span>)"</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Error in field '<span class="hljs-subst">{self.field}</span>': <span class="hljs-subst">{self.message}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">if</span> self.value <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"ValidationError(field='<span class="hljs-subst">{self.field}</span>', message='<span class="hljs-subst">{self.message}</span>', value=<span class="hljs-subst">{repr(self.value)}</span>)"</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"ValidationError(field='<span class="hljs-subst">{self.field}</span>', message='<span class="hljs-subst">{self.message}</span>')"</span>

<span class="hljs-comment"># Usage</span>
<span class="hljs-keyword">try</span>:
    age = <span class="hljs-number">-5</span>
    <span class="hljs-keyword">if</span> age &lt; <span class="hljs-number">0</span>:
        <span class="hljs-keyword">raise</span> ValidationError(<span class="hljs-string">"age"</span>, <span class="hljs-string">"Age must be positive"</span>, age)
<span class="hljs-keyword">except</span> ValidationError <span class="hljs-keyword">as</span> e:
    print(e)  <span class="hljs-comment"># Output: Error in field 'age': Age must be positive (got: -5)</span>
</code></pre>
<p>This custom error class provides several benefits:</p>
<ol>
<li><p>It includes the field name where the error occurred</p>
</li>
<li><p>It shows the actual value that caused the error</p>
</li>
<li><p>It provides both user-friendly and detailed error messages</p>
</li>
<li><p>It makes debugging easier by including all relevant information</p>
</li>
</ol>
<h2 id="heading-operator-overloading"><strong>Operator Overloading</strong></h2>
<p>Operator overloading is one of the most powerful features of Python's magic methods. It lets you define how your objects behave when used with operators like <code>+</code>, <code>-</code>, <code>*</code>, and <code>==</code>. This makes your code more intuitive and readable.</p>
<h3 id="heading-arithmetic-operators"><strong>Arithmetic Operators</strong></h3>
<p>Python provides magic methods for all basic arithmetic operations. Here's a table showing which method corresponds to which operator:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Operator</td><td>Magic Method</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>+</code></td><td><code>__add__</code></td><td>Addition</td></tr>
<tr>
<td><code>-</code></td><td><code>__sub__</code></td><td>Subtraction</td></tr>
<tr>
<td><code>*</code></td><td><code>__mul__</code></td><td>Multiplication</td></tr>
<tr>
<td><code>/</code></td><td><code>__truediv__</code></td><td>Division</td></tr>
<tr>
<td><code>//</code></td><td><code>__floordiv__</code></td><td>Floor division</td></tr>
<tr>
<td><code>%</code></td><td><code>__mod__</code></td><td>Modulo</td></tr>
<tr>
<td><code>**</code></td><td><code>__pow__</code></td><td>Exponentiation</td></tr>
</tbody>
</table>
</div><h3 id="heading-comparison-operators"><strong>Comparison Operators</strong></h3>
<p>Similarly, you can define how your objects are compared using these magic methods:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Operator</td><td>Magic Method</td><td>Description</td></tr>
</thead>
<tbody>
<tr>
<td><code>==</code></td><td><code>__eq__</code></td><td>Equal to</td></tr>
<tr>
<td><code>!=</code></td><td><code>__ne__</code></td><td>Not equal to</td></tr>
<tr>
<td><code>&lt;</code></td><td><code>__lt__</code></td><td>Less than</td></tr>
<tr>
<td><code>&gt;</code></td><td><code>__gt__</code></td><td>Greater than</td></tr>
<tr>
<td><code>&lt;=</code></td><td><code>__le__</code></td><td>Less than or equal to</td></tr>
<tr>
<td><code>&gt;=</code></td><td><code>__ge__</code></td><td>Greater than or equal to</td></tr>
</tbody>
</table>
</div><h3 id="heading-practical-example-money-class"><strong>Practical Example: Money Class</strong></h3>
<p>Let's create a <code>Money</code> class that handles currency operations correctly. This example shows how to implement multiple operators and handle edge cases:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> total_ordering
<span class="hljs-keyword">from</span> decimal <span class="hljs-keyword">import</span> Decimal

<span class="hljs-meta">@total_ordering  # Implements all comparison methods based on __eq__ and __lt__</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Money</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, amount, currency=<span class="hljs-string">"USD"</span></span>):</span>
        self.amount = Decimal(str(amount))
        self.currency = currency

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__add__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">if</span> self.currency != other.currency:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Cannot add different currencies: <span class="hljs-subst">{self.currency}</span> and <span class="hljs-subst">{other.currency}</span>"</span>)
        <span class="hljs-keyword">return</span> Money(self.amount + other.amount, self.currency)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__sub__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">if</span> self.currency != other.currency:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Cannot subtract different currencies: <span class="hljs-subst">{self.currency}</span> and <span class="hljs-subst">{other.currency}</span>"</span>)
        <span class="hljs-keyword">return</span> Money(self.amount - other.amount, self.currency)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__mul__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> isinstance(other, (int, float, Decimal)):
            <span class="hljs-keyword">return</span> Money(self.amount * Decimal(str(other)), self.currency)
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__truediv__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> isinstance(other, (int, float, Decimal)):
            <span class="hljs-keyword">return</span> Money(self.amount / Decimal(str(other)), self.currency)
        <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__eq__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">return</span> self.currency == other.currency <span class="hljs-keyword">and</span> self.amount == other.amount

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__lt__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">if</span> self.currency != other.currency:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Cannot compare different currencies: <span class="hljs-subst">{self.currency}</span> and <span class="hljs-subst">{other.currency}</span>"</span>)
        <span class="hljs-keyword">return</span> self.amount &lt; other.amount

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__str__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{self.currency}</span> <span class="hljs-subst">{self.amount:<span class="hljs-number">.2</span>f}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__repr__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Money(<span class="hljs-subst">{repr(float(self.amount))}</span>, <span class="hljs-subst">{repr(self.currency)}</span>)"</span>
</code></pre>
<p>Let's break down the key features of this <code>Money</code> class:</p>
<ol>
<li><p><strong>Precision handling</strong>: We use <code>Decimal</code> instead of <code>float</code> to avoid floating-point precision issues with money calculations.</p>
</li>
<li><p><strong>Currency safety</strong>: The class prevents operations between different currencies to avoid errors.</p>
</li>
<li><p><strong>Type checking</strong>: Each method checks if the other operand is of the correct type using <code>isinstance()</code>.</p>
</li>
<li><p><strong>NotImplemented</strong>: When an operation doesn't make sense, we return <code>NotImplemented</code> to let Python try the reverse operation.</p>
</li>
<li><p><strong>@total_ordering</strong>: This decorator automatically implements all comparison methods based on <code>__eq__</code> and <code>__lt__</code>.</p>
</li>
</ol>
<p>Here's how to use the <code>Money</code> class:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Basic arithmetic</span>
wallet = Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>)
expense = Money(<span class="hljs-number">20</span>, <span class="hljs-string">"USD"</span>)
remaining = wallet - expense
print(remaining)  <span class="hljs-comment"># Output: USD 80.00</span>

<span class="hljs-comment"># Working with different currencies</span>
salary = Money(<span class="hljs-number">5000</span>, <span class="hljs-string">"USD"</span>)
bonus = Money(<span class="hljs-number">1000</span>, <span class="hljs-string">"USD"</span>)
total = salary + bonus
print(total)  <span class="hljs-comment"># Output: USD 6000.00</span>

<span class="hljs-comment"># Division by scalar</span>
weekly_pay = salary / <span class="hljs-number">4</span>
print(weekly_pay)  <span class="hljs-comment"># Output: USD 1250.00</span>

<span class="hljs-comment"># Comparisons</span>
print(Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>) &gt; Money(<span class="hljs-number">50</span>, <span class="hljs-string">"USD"</span>))  <span class="hljs-comment"># Output: True</span>
print(Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>) == Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>))  <span class="hljs-comment"># Output: True</span>

<span class="hljs-comment"># Error handling</span>
<span class="hljs-keyword">try</span>:
    Money(<span class="hljs-number">100</span>, <span class="hljs-string">"USD"</span>) + Money(<span class="hljs-number">100</span>, <span class="hljs-string">"EUR"</span>)
<span class="hljs-keyword">except</span> ValueError <span class="hljs-keyword">as</span> e:
    print(e)  <span class="hljs-comment"># Output: Cannot add different currencies: USD and EUR</span>
</code></pre>
<p>This <code>Money</code> class demonstrates several important concepts:</p>
<ol>
<li><p>How to handle different types of operands</p>
</li>
<li><p>How to implement proper error handling</p>
</li>
<li><p>How to use the <code>@total_ordering</code> decorator</p>
</li>
<li><p>How to maintain precision in financial calculations</p>
</li>
<li><p>How to provide both string and representation methods</p>
</li>
</ol>
<h2 id="heading-container-methods"><strong>Container Methods</strong></h2>
<p>Container methods let you make your objects behave like built-in containers such as lists, dictionaries, or sets. This is particularly useful when you need custom behavior for storing and retrieving data.</p>
<h3 id="heading-sequence-protocol"><strong>Sequence Protocol</strong></h3>
<p>To make your object behave like a sequence (like a list or tuple), you need to implement these methods:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>Description</td><td>Example Usage</td></tr>
</thead>
<tbody>
<tr>
<td><code>__len__</code></td><td>Returns the length of the container</td><td><code>len(obj)</code></td></tr>
<tr>
<td><code>__getitem__</code></td><td>Allows indexing with <code>obj[key]</code></td><td><code>obj[0]</code></td></tr>
<tr>
<td><code>__setitem__</code></td><td>Allows assignment with <code>obj[key] = value</code></td><td><code>obj[0] = 42</code></td></tr>
<tr>
<td><code>__delitem__</code></td><td>Allows deletion with <code>del obj[key]</code></td><td><code>del obj[0]</code></td></tr>
<tr>
<td><code>__iter__</code></td><td>Returns an iterator for the container</td><td><code>for item in obj:</code></td></tr>
<tr>
<td><code>__contains__</code></td><td>Implements the <code>in</code> operator</td><td><code>42 in obj</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-mapping-protocol"><strong>Mapping Protocol</strong></h3>
<p>For dictionary-like behavior, you'll want to implement these methods:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Method</td><td>Description</td><td>Example Usage</td></tr>
</thead>
<tbody>
<tr>
<td><code>__getitem__</code></td><td>Get value by key</td><td><code>obj["key"]</code></td></tr>
<tr>
<td><code>__setitem__</code></td><td>Set value by key</td><td><code>obj["key"] = value</code></td></tr>
<tr>
<td><code>__delitem__</code></td><td>Delete key-value pair</td><td><code>del obj["key"]</code></td></tr>
<tr>
<td><code>__len__</code></td><td>Get number of key-value pairs</td><td><code>len(obj)</code></td></tr>
<tr>
<td><code>__iter__</code></td><td>Iterate over keys</td><td><code>for key in obj:</code></td></tr>
<tr>
<td><code>__contains__</code></td><td>Check if key exists</td><td><code>"key" in obj</code></td></tr>
</tbody>
</table>
</div><h3 id="heading-practical-example-custom-cache"><strong>Practical Example: Custom Cache</strong></h3>
<p>Let's implement a time-based cache that automatically expires old entries. This example shows how to create a custom container that behaves like a dictionary but with additional functionality:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> collections <span class="hljs-keyword">import</span> OrderedDict

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExpiringCache</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, max_age_seconds=<span class="hljs-number">60</span></span>):</span>
        self.max_age = max_age_seconds
        self._cache = OrderedDict()  <span class="hljs-comment"># {key: (value, timestamp)}</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">if</span> key <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._cache:
            <span class="hljs-keyword">raise</span> KeyError(key)

        value, timestamp = self._cache[key]
        <span class="hljs-keyword">if</span> time.time() - timestamp &gt; self.max_age:
            <span class="hljs-keyword">del</span> self._cache[key]
            <span class="hljs-keyword">raise</span> KeyError(<span class="hljs-string">f"Key '<span class="hljs-subst">{key}</span>' has expired"</span>)

        <span class="hljs-keyword">return</span> value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__setitem__</span>(<span class="hljs-params">self, key, value</span>):</span>
        self._cache[key] = (value, time.time())
        self._cache.move_to_end(key)  <span class="hljs-comment"># Move to end to maintain insertion order</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__delitem__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">del</span> self._cache[key]

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__len__</span>(<span class="hljs-params">self</span>):</span>
        self._clean_expired()  <span class="hljs-comment"># Clean expired items before reporting length</span>
        <span class="hljs-keyword">return</span> len(self._cache)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__iter__</span>(<span class="hljs-params">self</span>):</span>
        self._clean_expired()  <span class="hljs-comment"># Clean expired items before iteration</span>
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> self._cache:
            <span class="hljs-keyword">yield</span> key

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__contains__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">if</span> key <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._cache:
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>

        _, timestamp = self._cache[key]
        <span class="hljs-keyword">if</span> time.time() - timestamp &gt; self.max_age:
            <span class="hljs-keyword">del</span> self._cache[key]
            <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>

        <span class="hljs-keyword">return</span> <span class="hljs-literal">True</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">_clean_expired</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Remove all expired entries from the cache."""</span>
        now = time.time()
        expired_keys = [
            key <span class="hljs-keyword">for</span> key, (_, timestamp) <span class="hljs-keyword">in</span> self._cache.items()
            <span class="hljs-keyword">if</span> now - timestamp &gt; self.max_age
        ]
        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> expired_keys:
            <span class="hljs-keyword">del</span> self._cache[key]
</code></pre>
<p>Let's break down how this cache works:</p>
<ol>
<li><p><strong>Storage</strong>: The cache uses an <code>OrderedDict</code> to store key-value pairs along with timestamps.</p>
</li>
<li><p><strong>Expiration</strong>: Each value is stored as a tuple of <code>(value, timestamp)</code>. When accessing a value, we check if it has expired.</p>
</li>
<li><p><strong>Container methods</strong>: The class implements all necessary methods to behave like a dictionary:</p>
<ul>
<li><p><code>__getitem__</code>: Retrieves values and checks expiration</p>
</li>
<li><p><code>__setitem__</code>: Stores values with current timestamp</p>
</li>
<li><p><code>__delitem__</code>: Removes entries</p>
</li>
<li><p><code>__len__</code>: Returns number of non-expired entries</p>
</li>
<li><p><code>__iter__</code>: Iterates over non-expired keys</p>
</li>
<li><p><code>__contains__</code>: Checks if a key exists</p>
</li>
</ul>
</li>
</ol>
<p>Here's how to use the cache:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a cache with 2-second expiration</span>
cache = ExpiringCache(max_age_seconds=<span class="hljs-number">2</span>)

<span class="hljs-comment"># Store some values</span>
cache[<span class="hljs-string">"name"</span>] = <span class="hljs-string">"Vivek"</span>
cache[<span class="hljs-string">"age"</span>] = <span class="hljs-number">30</span>

<span class="hljs-comment"># Access values</span>
print(<span class="hljs-string">"name"</span> <span class="hljs-keyword">in</span> cache)  <span class="hljs-comment"># Output: True</span>
print(cache[<span class="hljs-string">"name"</span>])    <span class="hljs-comment"># Output: Vivek</span>
print(len(cache))       <span class="hljs-comment"># Output: 2</span>

<span class="hljs-comment"># Wait for expiration</span>
print(<span class="hljs-string">"Waiting for expiration..."</span>)
time.sleep(<span class="hljs-number">3</span>)

<span class="hljs-comment"># Check expired values</span>
print(<span class="hljs-string">"name"</span> <span class="hljs-keyword">in</span> cache)  <span class="hljs-comment"># Output: False</span>
<span class="hljs-keyword">try</span>:
    print(cache[<span class="hljs-string">"name"</span>])
<span class="hljs-keyword">except</span> KeyError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"KeyError: <span class="hljs-subst">{e}</span>"</span>)  <span class="hljs-comment"># Output: KeyError: 'name'</span>

print(len(cache))  <span class="hljs-comment"># Output: 0</span>
</code></pre>
<p>This cache implementation provides several benefits:</p>
<ol>
<li><p>Automatic expiration of old entries</p>
</li>
<li><p>Dictionary-like interface for easy use</p>
</li>
<li><p>Memory efficiency by removing expired entries</p>
</li>
<li><p>Thread-safe operations (assuming single-threaded access)</p>
</li>
<li><p>Maintains insertion order of entries</p>
</li>
</ol>
<h2 id="heading-attribute-access"><strong>Attribute Access</strong></h2>
<p>Attribute access methods let you control how your objects handle getting, setting, and deleting attributes. This is particularly useful for implementing properties, validation, and logging.</p>
<h3 id="heading-getattr-and-getattribute"><strong>getattr and getattribute</strong></h3>
<p>Python provides two methods for controlling attribute access:</p>
<ol>
<li><p><code>__getattr__</code>: Called only when an attribute lookup fails (that is, when the attribute doesn't exist)</p>
</li>
<li><p><code>__getattribute__</code>: Called for every attribute access, even for attributes that exist</p>
</li>
</ol>
<p>The key difference is that <code>__getattribute__</code> is called for all attribute access, while <code>__getattr__</code> is only called when the attribute isn't found through normal means.</p>
<p>Here's a simple example showing the difference:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AttributeDemo</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.name = <span class="hljs-string">"Vivek"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span>(<span class="hljs-params">self, name</span>):</span>
        print(<span class="hljs-string">f"__getattr__ called for <span class="hljs-subst">{name}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Default value for <span class="hljs-subst">{name}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattribute__</span>(<span class="hljs-params">self, name</span>):</span>
        print(<span class="hljs-string">f"__getattribute__ called for <span class="hljs-subst">{name}</span>"</span>)
        <span class="hljs-keyword">return</span> super().__getattribute__(name)

demo = AttributeDemo()
print(demo.name)      <span class="hljs-comment"># Output: __getattribute__ called for name</span>
                      <span class="hljs-comment">#        Vivek</span>
print(demo.age)       <span class="hljs-comment"># Output: __getattribute__ called for age</span>
                      <span class="hljs-comment">#        __getattr__ called for age</span>
                      <span class="hljs-comment">#        Default value for age</span>
</code></pre>
<h3 id="heading-setattr-and-delattr"><strong>setattr and delattr</strong></h3>
<p>Similarly, you can control how attributes are set and deleted:</p>
<ol>
<li><p><code>__setattr__</code>: Called when an attribute is set</p>
</li>
<li><p><code>__delattr__</code>: Called when an attribute is deleted</p>
</li>
</ol>
<p>These methods let you implement validation, logging, or custom behavior when attributes are modified.</p>
<h3 id="heading-practical-example-auto-logging-properties"><strong>Practical Example: Auto-Logging Properties</strong></h3>
<p>Let's create a class that automatically logs all property changes. This is useful for debugging, auditing, or tracking object state changes:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging

<span class="hljs-comment"># Set up logging</span>
logging.basicConfig(
    level=logging.INFO,
    format=<span class="hljs-string">'%(asctime)s - %(levelname)s - %(message)s'</span>
)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">LoggedObject</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, **kwargs</span>):</span>
        self._data = {}
        <span class="hljs-comment"># Initialize attributes without triggering __setattr__</span>
        <span class="hljs-keyword">for</span> key, value <span class="hljs-keyword">in</span> kwargs.items():
            self._data[key] = value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span>(<span class="hljs-params">self, name</span>):</span>
        <span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> self._data:
            logging.debug(<span class="hljs-string">f"Accessing attribute <span class="hljs-subst">{name}</span>: <span class="hljs-subst">{self._data[name]}</span>"</span>)
            <span class="hljs-keyword">return</span> self._data[name]
        <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">f"'<span class="hljs-subst">{self.__class__.__name__}</span>' object has no attribute '<span class="hljs-subst">{name}</span>'"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__setattr__</span>(<span class="hljs-params">self, name, value</span>):</span>
        <span class="hljs-keyword">if</span> name == <span class="hljs-string">"_data"</span>:
            <span class="hljs-comment"># Allow setting the _data attribute directly</span>
            super().__setattr__(name, value)
        <span class="hljs-keyword">else</span>:
            old_value = self._data.get(name, <span class="hljs-string">"&lt;undefined&gt;"</span>)
            self._data[name] = value
            logging.info(<span class="hljs-string">f"Changed <span class="hljs-subst">{name}</span>: <span class="hljs-subst">{old_value}</span> -&gt; <span class="hljs-subst">{value}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__delattr__</span>(<span class="hljs-params">self, name</span>):</span>
        <span class="hljs-keyword">if</span> name <span class="hljs-keyword">in</span> self._data:
            old_value = self._data[name]
            <span class="hljs-keyword">del</span> self._data[name]
            logging.info(<span class="hljs-string">f"Deleted <span class="hljs-subst">{name}</span> (was: <span class="hljs-subst">{old_value}</span>)"</span>)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">f"'<span class="hljs-subst">{self.__class__.__name__}</span>' object has no attribute '<span class="hljs-subst">{name}</span>'"</span>)
</code></pre>
<p>Let's break down how this class works:</p>
<ol>
<li><p><strong>Storage</strong>: The class uses a private <code>_data</code> dictionary to store attribute values.</p>
</li>
<li><p><strong>Attribute access</strong>:</p>
<ul>
<li><p><code>__getattr__</code>: Returns values from <code>_data</code> and logs debug messages</p>
</li>
<li><p><code>__setattr__</code>: Stores values in <code>_data</code> and logs changes</p>
</li>
<li><p><code>__delattr__</code>: Removes values from <code>_data</code> and logs deletions</p>
</li>
</ul>
</li>
<li><p><strong>Special handling</strong>: The <code>_data</code> attribute itself is handled differently to avoid infinite recursion.</p>
</li>
</ol>
<p>Here's how to use the class:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a logged object with initial values</span>
user = LoggedObject(name=<span class="hljs-string">"Vivek"</span>, email=<span class="hljs-string">"hello@wewake.dev"</span>)

<span class="hljs-comment"># Modify attributes</span>
user.name = <span class="hljs-string">"Vivek"</span>  <span class="hljs-comment"># Logs: Changed name: Vivek -&gt; Vivek</span>
user.age = <span class="hljs-number">30</span>         <span class="hljs-comment"># Logs: Changed age: &lt;undefined&gt; -&gt; 30</span>

<span class="hljs-comment"># Access attributes</span>
print(user.name)      <span class="hljs-comment"># Output: Vivek</span>

<span class="hljs-comment"># Delete attributes</span>
<span class="hljs-keyword">del</span> user.email        <span class="hljs-comment"># Logs: Deleted email (was: hello@wewake.dev)</span>

<span class="hljs-comment"># Try to access deleted attribute</span>
<span class="hljs-keyword">try</span>:
    print(user.email)
<span class="hljs-keyword">except</span> AttributeError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"AttributeError: <span class="hljs-subst">{e}</span>"</span>)  <span class="hljs-comment"># Output: AttributeError: 'LoggedObject' object has no attribute 'email'</span>
</code></pre>
<p>This implementation provides several benefits:</p>
<ol>
<li><p>Automatic logging of all attribute changes</p>
</li>
<li><p>Debug-level logging for attribute access</p>
</li>
<li><p>Clear error messages for missing attributes</p>
</li>
<li><p>Easy tracking of object state changes</p>
</li>
<li><p>Useful for debugging and auditing</p>
</li>
</ol>
<h2 id="heading-context-managers"><strong>Context Managers</strong></h2>
<p>Context managers are a powerful feature in Python that helps you manage resources properly. They ensure that resources are properly acquired and released, even if an error occurs. The <code>with</code> statement is the most common way to use context managers.</p>
<h3 id="heading-enter-and-exit"><strong>enter and exit</strong></h3>
<p>To create a context manager, you need to implement two magic methods:</p>
<ol>
<li><p><code>__enter__</code>: Called when entering the <code>with</code> block. It should return the resource to be managed.</p>
</li>
<li><p><code>__exit__</code>: Called when exiting the <code>with</code> block, even if an exception occurs. It should handle cleanup.</p>
</li>
</ol>
<p>The <code>__exit__</code> method receives three arguments:</p>
<ul>
<li><p><code>exc_type</code>: The type of the exception (if any)</p>
</li>
<li><p><code>exc_val</code>: The exception instance (if any)</p>
</li>
<li><p><code>exc_tb</code>: The traceback (if any)</p>
</li>
</ul>
<h3 id="heading-practical-example-database-connection-manager"><strong>Practical Example: Database Connection Manager</strong></h3>
<p>Let's create a context manager for database connections. This example shows how to properly manage database resources and handle transactions:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sqlite3
<span class="hljs-keyword">import</span> logging

<span class="hljs-comment"># Set up logging</span>
logging.basicConfig(
    level=logging.INFO,
    format=<span class="hljs-string">'%(asctime)s - %(levelname)s - %(message)s'</span>
)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, db_path</span>):</span>
        self.db_path = db_path
        self.connection = <span class="hljs-literal">None</span>
        self.cursor = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__enter__</span>(<span class="hljs-params">self</span>):</span>
        logging.info(<span class="hljs-string">f"Connecting to database: <span class="hljs-subst">{self.db_path}</span>"</span>)
        self.connection = sqlite3.connect(self.db_path)
        self.cursor = self.connection.cursor()
        <span class="hljs-keyword">return</span> self.cursor

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__exit__</span>(<span class="hljs-params">self, exc_type, exc_val, exc_tb</span>):</span>
        <span class="hljs-keyword">if</span> exc_type <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            logging.error(<span class="hljs-string">f"An error occurred: <span class="hljs-subst">{exc_val}</span>"</span>)
            self.connection.rollback()
            logging.info(<span class="hljs-string">"Transaction rolled back"</span>)
        <span class="hljs-keyword">else</span>:
            self.connection.commit()
            logging.info(<span class="hljs-string">"Transaction committed"</span>)

        <span class="hljs-keyword">if</span> self.cursor:
            self.cursor.close()
        <span class="hljs-keyword">if</span> self.connection:
            self.connection.close()

        logging.info(<span class="hljs-string">"Database connection closed"</span>)

        <span class="hljs-comment"># Return False to propagate exceptions, True to suppress them</span>
        <span class="hljs-keyword">return</span> <span class="hljs-literal">False</span>
</code></pre>
<p>Let's break down how this context manager works:</p>
<ol>
<li><p><strong>Initialization</strong>:</p>
<ul>
<li><p>The class takes a database path</p>
</li>
<li><p>It initializes connection and cursor as None</p>
</li>
</ul>
</li>
<li><p><strong>Enter method</strong>:</p>
<ul>
<li><p>Creates a database connection</p>
</li>
<li><p>Creates a cursor</p>
</li>
<li><p>Returns the cursor for use in the <code>with</code> block</p>
</li>
</ul>
</li>
<li><p><strong>Exit method</strong>:</p>
<ul>
<li><p>Handles transaction management (commit/rollback)</p>
</li>
<li><p>Closes cursor and connection</p>
</li>
<li><p>Logs all operations</p>
</li>
<li><p>Returns False to propagate exceptions</p>
</li>
</ul>
</li>
</ol>
<p>Here's how to use the context manager:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a test database in memory</span>
<span class="hljs-keyword">try</span>:
    <span class="hljs-comment"># Successful transaction</span>
    <span class="hljs-keyword">with</span> DatabaseConnection(<span class="hljs-string">":memory:"</span>) <span class="hljs-keyword">as</span> cursor:
        <span class="hljs-comment"># Create a table</span>
        cursor.execute(<span class="hljs-string">"""
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT,
                email TEXT
            )
        """</span>)

        <span class="hljs-comment"># Insert data</span>
        cursor.execute(
            <span class="hljs-string">"INSERT INTO users (name, email) VALUES (?, ?)"</span>,
            (<span class="hljs-string">"Vivek"</span>, <span class="hljs-string">"hello@wewake.dev"</span>)
        )

        <span class="hljs-comment"># Query data</span>
        cursor.execute(<span class="hljs-string">"SELECT * FROM users"</span>)
        print(cursor.fetchall())  <span class="hljs-comment"># Output: [(1, 'Vivek', 'hello@wewake.dev')]</span>

    <span class="hljs-comment"># Demonstrate transaction rollback on error</span>
    <span class="hljs-keyword">with</span> DatabaseConnection(<span class="hljs-string">":memory:"</span>) <span class="hljs-keyword">as</span> cursor:
        cursor.execute(<span class="hljs-string">"""
            CREATE TABLE users (
                id INTEGER PRIMARY KEY,
                name TEXT,
                email TEXT
            )
        """</span>)
        cursor.execute(
            <span class="hljs-string">"INSERT INTO users (name, email) VALUES (?, ?)"</span>,
            (<span class="hljs-string">"Wewake"</span>, <span class="hljs-string">"contact@wewake.dev"</span>)
        )
        <span class="hljs-comment"># This will cause an error - table 'nonexistent' doesn't exist</span>
        cursor.execute(<span class="hljs-string">"SELECT * FROM nonexistent"</span>)
<span class="hljs-keyword">except</span> sqlite3.OperationalError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"Caught exception: <span class="hljs-subst">{e}</span>"</span>)
</code></pre>
<p>This context manager provides several benefits:</p>
<ol>
<li><p>Resources are managed automatically (ex: connections are always closed).</p>
</li>
<li><p>With transaction safety, changes are committed or rolled back appropriately.</p>
</li>
<li><p>Exceptions are caught and handled gracefully</p>
</li>
<li><p>All operations are logged for debugging</p>
</li>
<li><p>The <code>with</code> statement makes the code clear and concise</p>
</li>
</ol>
<h2 id="heading-callable-objects"><strong>Callable Objects</strong></h2>
<p>The <code>__call__</code> magic method lets you make instances of your class behave like functions. This is useful for creating objects that maintain state between calls or for implementing function-like behavior with additional features.</p>
<h3 id="heading-call"><strong>call</strong></h3>
<p>The <code>__call__</code> method is called when you try to call an instance of your class as if it were a function. Here's a simple example:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Multiplier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, factor</span>):</span>
        self.factor = factor

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, x</span>):</span>
        <span class="hljs-keyword">return</span> x * self.factor

<span class="hljs-comment"># Create instances that behave like functions</span>
double = Multiplier(<span class="hljs-number">2</span>)
triple = Multiplier(<span class="hljs-number">3</span>)

print(double(<span class="hljs-number">5</span>))  <span class="hljs-comment"># Output: 10</span>
print(triple(<span class="hljs-number">5</span>))  <span class="hljs-comment"># Output: 15</span>
</code></pre>
<p>This example shows how <code>__call__</code> lets you create objects that maintain state (the factor) while being callable like functions.</p>
<h3 id="heading-practical-example-memoization-decorator"><strong>Practical Example: Memoization Decorator</strong></h3>
<p>Let's implement a memoization decorator using <code>__call__</code>. This decorator will cache function results to avoid redundant computations:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> time
<span class="hljs-keyword">import</span> functools

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Memoize</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, func</span>):</span>
        self.func = func
        self.cache = {}
        <span class="hljs-comment"># Preserve function metadata (name, docstring, etc.)</span>
        functools.update_wrapper(self, func)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">self, *args, **kwargs</span>):</span>
        <span class="hljs-comment"># Create a key from the arguments</span>
        <span class="hljs-comment"># For simplicity, we assume all arguments are hashable</span>
        key = str(args) + str(sorted(kwargs.items()))

        <span class="hljs-keyword">if</span> key <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self.cache:
            self.cache[key] = self.func(*args, **kwargs)

        <span class="hljs-keyword">return</span> self.cache[key]

<span class="hljs-comment"># Usage</span>
<span class="hljs-meta">@Memoize</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fibonacci</span>(<span class="hljs-params">n</span>):</span>
    <span class="hljs-string">"""Calculate the nth Fibonacci number recursively."""</span>
    <span class="hljs-keyword">if</span> n &lt;= <span class="hljs-number">1</span>:
        <span class="hljs-keyword">return</span> n
    <span class="hljs-keyword">return</span> fibonacci(n<span class="hljs-number">-1</span>) + fibonacci(n<span class="hljs-number">-2</span>)

<span class="hljs-comment"># Measure execution time</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">time_execution</span>(<span class="hljs-params">func, *args, **kwargs</span>):</span>
    start = time.time()
    result = func(*args, **kwargs)
    end = time.time()
    print(<span class="hljs-string">f"<span class="hljs-subst">{func.__name__}</span>(<span class="hljs-subst">{args}</span>, <span class="hljs-subst">{kwargs}</span>) took <span class="hljs-subst">{end - start:<span class="hljs-number">.6</span>f}</span> seconds"</span>)
    <span class="hljs-keyword">return</span> result

<span class="hljs-comment"># Without memoization, this would be extremely slow</span>
print(<span class="hljs-string">"Calculating fibonacci(35)..."</span>)
result = time_execution(fibonacci, <span class="hljs-number">35</span>)
print(<span class="hljs-string">f"Result: <span class="hljs-subst">{result}</span>"</span>)

<span class="hljs-comment"># Second call is instant due to memoization</span>
print(<span class="hljs-string">"\nCalculating fibonacci(35) again..."</span>)
result = time_execution(fibonacci, <span class="hljs-number">35</span>)
print(<span class="hljs-string">f"Result: <span class="hljs-subst">{result}</span>"</span>)
</code></pre>
<p>Let's break down how this memoization decorator works:</p>
<ol>
<li><p><strong>Initialization</strong>:</p>
<ul>
<li><p>Takes a function as an argument</p>
</li>
<li><p>Creates a cache dictionary to store results</p>
</li>
<li><p>Preserves the function's metadata using <code>functools.update_wrapper</code></p>
</li>
</ul>
</li>
<li><p><strong>Call method</strong>:</p>
<ul>
<li><p>Creates a unique key from the function arguments</p>
</li>
<li><p>Checks if the result is in the cache</p>
</li>
<li><p>If not, computes the result and stores it</p>
</li>
<li><p>Returns the cached result</p>
</li>
</ul>
</li>
<li><p><strong>Usage</strong>:</p>
<ul>
<li><p>Applied as a decorator to any function</p>
</li>
<li><p>Automatically caches results for repeated calls</p>
</li>
<li><p>Preserves function metadata and behavior</p>
</li>
</ul>
</li>
</ol>
<p>The benefits of this implementation include:</p>
<ol>
<li><p>Better performance, as it avoids redundant computations</p>
</li>
<li><p>Better, transparency, as it works without modifying the original function</p>
</li>
<li><p>It’s flexible, and can be used with any function</p>
</li>
<li><p>It’s memory efficient and caches results for reuse</p>
</li>
<li><p>It maintains function documentation</p>
</li>
</ol>
<h2 id="heading-advanced-magic-methods"><strong>Advanced Magic Methods</strong></h2>
<p>Now let's explore some of Python's more advanced magic methods. These methods give you fine-grained control over object creation, memory usage, and dictionary behavior.</p>
<h3 id="heading-new-for-object-creation"><strong>new for Object Creation</strong></h3>
<p>The <code>__new__</code> method is called before <code>__init__</code> and is responsible for creating and returning a new instance of the class. This is useful for implementing patterns like singletons or immutable objects.</p>
<p>Here's an example of a singleton pattern using <code>__new__</code>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Singleton</span>:</span>
    _instance = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__new__</span>(<span class="hljs-params">cls, *args, **kwargs</span>):</span>
        <span class="hljs-keyword">if</span> cls._instance <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
            cls._instance = super().__new__(cls)
        <span class="hljs-keyword">return</span> cls._instance

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name=None</span>):</span>
        <span class="hljs-comment"># This will be called every time Singleton() is called</span>
        <span class="hljs-keyword">if</span> name <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            self.name = name

<span class="hljs-comment"># Usage</span>
s1 = Singleton(<span class="hljs-string">"Vivek"</span>)
s2 = Singleton(<span class="hljs-string">"Wewake"</span>)
print(s1 <span class="hljs-keyword">is</span> s2)  <span class="hljs-comment"># Output: True</span>
print(s1.name)   <span class="hljs-comment"># Output: Wewake (the second initialization overwrote the first)</span>
</code></pre>
<p>Let's break down how this singleton works:</p>
<ol>
<li><p><strong>Class variable</strong>: <code>_instance</code> stores the single instance of the class</p>
</li>
<li><p><strong>new</strong> method:</p>
<ul>
<li><p>Checks if an instance exists</p>
</li>
<li><p>Creates one if it doesn't</p>
</li>
<li><p>Returns the existing instance if it does</p>
</li>
</ul>
</li>
<li><p><strong>init</strong> method:</p>
<ul>
<li><p>Called every time the constructor is used</p>
</li>
<li><p>Updates the instance's attributes</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-slots-for-memory-optimization"><strong>slots for Memory Optimization</strong></h3>
<p>The <code>__slots__</code> class variable restricts which attributes an instance can have, saving memory. This is particularly useful when you have many instances of a class with a fixed set of attributes.</p>
<p>Here's a comparison of regular and slotted classes:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> sys

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegularPerson</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name, age, email</span>):</span>
        self.name = name
        self.age = age
        self.email = email

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SlottedPerson</span>:</span>
    __slots__ = [<span class="hljs-string">'name'</span>, <span class="hljs-string">'age'</span>, <span class="hljs-string">'email'</span>]

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name, age, email</span>):</span>
        self.name = name
        self.age = age
        self.email = email

<span class="hljs-comment"># Compare memory usage</span>
regular_people = [RegularPerson(<span class="hljs-string">"Vivek"</span> + str(i), <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000</span>)]
slotted_people = [SlottedPerson(<span class="hljs-string">"Vivek"</span> + str(i), <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>) <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000</span>)]

print(<span class="hljs-string">f"Regular person size: <span class="hljs-subst">{sys.getsizeof(regular_people[<span class="hljs-number">0</span>])}</span> bytes"</span>)  <span class="hljs-comment"># Output: Regular person size: 48 bytes</span>
print(<span class="hljs-string">f"Slotted person size: <span class="hljs-subst">{sys.getsizeof(slotted_people[<span class="hljs-number">0</span>])}</span> bytes"</span>)  <span class="hljs-comment"># Output: Slotted person size: 56 bytes</span>
print(<span class="hljs-string">f"Memory saved per instance: <span class="hljs-subst">{sys.getsizeof(regular_people[<span class="hljs-number">0</span>]) - sys.getsizeof(slotted_people[<span class="hljs-number">0</span>])}</span> bytes"</span>)  <span class="hljs-comment"># Output: Memory saved per instance: -8 bytes</span>
print(<span class="hljs-string">f"Total memory saved for 1000 instances: <span class="hljs-subst">{(sys.getsizeof(regular_people[<span class="hljs-number">0</span>]) - sys.getsizeof(slotted_people[<span class="hljs-number">0</span>])) * <span class="hljs-number">1000</span> / <span class="hljs-number">1024</span>:<span class="hljs-number">.2</span>f}</span> KB"</span>)  <span class="hljs-comment"># Output: Total memory saved for 1000 instances: -7.81 KB</span>
</code></pre>
<p>Running this code produces an interesting result:</p>
<pre><code class="lang-plaintext">Regular person size: 48 bytes
Slotted person size: 56 bytes
Memory saved per instance: -8 bytes
Total memory saved for 1000 instances: -7.81 KB
</code></pre>
<p>Surprisingly, in this simple example, the slotted instance is actually 8 bytes larger than the regular instance! This seems to contradict the common advice about <code>__slots__</code> saving memory.</p>
<p>So what's going on here? The real memory savings from <code>__slots__</code> come from:</p>
<ol>
<li><p>Eliminating dictionaries: Regular Python objects store their attributes in a dictionary (<code>__dict__</code>), which has overhead. The <code>sys.getsizeof()</code> function doesn't account for this dictionary's size.</p>
</li>
<li><p>Storing attributes: For small objects with few attributes, the overhead of the slot descriptors can outweigh the dictionary savings.</p>
</li>
<li><p>Scalability: The real benefit appears when:</p>
<ul>
<li><p>You have many instances (thousands or millions)</p>
</li>
<li><p>Your objects have many attributes</p>
</li>
<li><p>You're adding attributes dynamically</p>
</li>
</ul>
</li>
</ol>
<p>Let's see a more complete comparison:</p>
<pre><code class="lang-python"><span class="hljs-comment"># A more accurate memory measurement</span>
<span class="hljs-keyword">import</span> sys

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_size</span>(<span class="hljs-params">obj</span>):</span>
    <span class="hljs-string">"""Get a better estimate of the object's size in bytes."""</span>
    size = sys.getsizeof(obj)
    <span class="hljs-keyword">if</span> hasattr(obj, <span class="hljs-string">'__dict__'</span>):
        size += sys.getsizeof(obj.__dict__)
        <span class="hljs-comment"># Add the size of the dict contents</span>
        size += sum(sys.getsizeof(v) <span class="hljs-keyword">for</span> v <span class="hljs-keyword">in</span> obj.__dict__.values())
    <span class="hljs-keyword">return</span> size

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RegularPerson</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name, age, email</span>):</span>
        self.name = name
        self.age = age
        self.email = email

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SlottedPerson</span>:</span>
    __slots__ = [<span class="hljs-string">'name'</span>, <span class="hljs-string">'age'</span>, <span class="hljs-string">'email'</span>]

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name, age, email</span>):</span>
        self.name = name
        self.age = age
        self.email = email

regular = RegularPerson(<span class="hljs-string">"Vivek"</span>, <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>)
slotted = SlottedPerson(<span class="hljs-string">"Vivek"</span>, <span class="hljs-number">30</span>, <span class="hljs-string">"hello@wewake.dev"</span>)

print(<span class="hljs-string">f"Complete Regular person size: <span class="hljs-subst">{get_size(regular)}</span> bytes"</span>)  <span class="hljs-comment"># Output: Complete Regular person size: 610 bytes</span>
print(<span class="hljs-string">f"Complete Slotted person size: <span class="hljs-subst">{get_size(slotted)}</span> bytes"</span>)  <span class="hljs-comment"># Output: Complete Slotted person size: 56 bytes</span>
</code></pre>
<p>With this more accurate measurement, you'll see that slotted objects typically use less total memory, especially as you add more attributes.</p>
<p>Key points about <code>__slots__</code>:</p>
<ol>
<li><p><strong>Real memory benefits</strong>: The primary memory savings come from eliminating the instance <code>__dict__</code></p>
</li>
<li><p><strong>Dynamic restrictions</strong>: You can't add arbitrary attributes to slotted objects</p>
</li>
<li><p><strong>Inheritance considerations</strong>: Using <code>__slots__</code> with inheritance requires careful planning</p>
</li>
<li><p><strong>Use cases</strong>: Best for classes with many instances and fixed attributes</p>
</li>
<li><p><strong>Performance bonus</strong>: Can also provide faster attribute access in some cases</p>
</li>
</ol>
<h3 id="heading-missing-for-default-dictionary-values"><strong>missing for Default Dictionary Values</strong></h3>
<p>The <code>__missing__</code> method is called by dictionary subclasses when a key is not found. This is useful for implementing dictionaries with default values or automatic key creation.</p>
<p>Here's an example of a dictionary that automatically creates empty lists for missing keys:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AutoKeyDict</span>(<span class="hljs-params">dict</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__missing__</span>(<span class="hljs-params">self, key</span>):</span>
        self[key] = []
        <span class="hljs-keyword">return</span> self[key]

<span class="hljs-comment"># Usage</span>
groups = AutoKeyDict()
groups[<span class="hljs-string">"team1"</span>].append(<span class="hljs-string">"Vivek"</span>)
groups[<span class="hljs-string">"team1"</span>].append(<span class="hljs-string">"Wewake"</span>)
groups[<span class="hljs-string">"team2"</span>].append(<span class="hljs-string">"Vibha"</span>)

print(groups)  <span class="hljs-comment"># Output: {'team1': ['Vivek', 'Wewake'], 'team2': ['Vibha']}</span>
</code></pre>
<p>This implementation provides several benefits:</p>
<ol>
<li><p>No need to check if a key exists, which is more convenient.</p>
</li>
<li><p>Automatic initialization creates default values as needed.</p>
</li>
<li><p>Reduces boilerplate for dictionary initialization.</p>
</li>
<li><p>It’s more flexible, and can implement any default value logic.</p>
</li>
<li><p>Only creates values when needed, making it more memory efficient.</p>
</li>
</ol>
<h2 id="heading-performance-considerations"><strong>Performance Considerations</strong></h2>
<p>While magic methods are powerful, they can impact performance if you don’t use them carefully. Let's explore some common performance considerations and how to measure them.</p>
<h3 id="heading-impact-of-magic-methods-on-performance"><strong>Impact of Magic Methods on Performance</strong></h3>
<p>Different magic methods have different performance implications:</p>
<p><strong>Attribute Access methods</strong>:</p>
<ul>
<li><p><code>__getattr__</code>, <code>__getattribute__</code>, <code>__setattr__</code>, and <code>__delattr__</code> are called frequently</p>
</li>
<li><p>Complex operations in these methods can significantly slow down your code</p>
</li>
</ul>
<p><strong>Container methods</strong>:</p>
<ul>
<li><p><code>__getitem__</code>, <code>__setitem__</code>, and <code>__len__</code> are called often in loops</p>
</li>
<li><p>Inefficient implementations can make your container much slower than built-in types</p>
</li>
</ul>
<p><strong>Operator overloading</strong>:</p>
<ul>
<li><p>Arithmetic and comparison operators are used frequently</p>
</li>
<li><p>Complex implementations can make simple operations unexpectedly slow</p>
</li>
</ul>
<p>Let's measure the performance impact of <code>__getattr__</code> vs. direct attribute access:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> time

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DirectAccess</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.value = <span class="hljs-number">42</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GetAttrAccess</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self._value = <span class="hljs-number">42</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getattr__</span>(<span class="hljs-params">self, name</span>):</span>
        <span class="hljs-keyword">if</span> name == <span class="hljs-string">"value"</span>:
            <span class="hljs-keyword">return</span> self._value
        <span class="hljs-keyword">raise</span> AttributeError(<span class="hljs-string">f"'<span class="hljs-subst">{self.__class__.__name__}</span>' object has no attribute '<span class="hljs-subst">{name}</span>'"</span>)

<span class="hljs-comment"># Measure performance</span>
direct = DirectAccess()
getattr_obj = GetAttrAccess()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">benchmark</span>(<span class="hljs-params">obj, iterations=<span class="hljs-number">1000000</span></span>):</span>
    start = time.time()
    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(iterations):
        x = obj.value
    end = time.time()
    <span class="hljs-keyword">return</span> end - start

direct_time = benchmark(direct)
getattr_time = benchmark(getattr_obj)

print(<span class="hljs-string">f"Direct access: <span class="hljs-subst">{direct_time:<span class="hljs-number">.6</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"__getattr__ access: <span class="hljs-subst">{getattr_time:<span class="hljs-number">.6</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"__getattr__ is <span class="hljs-subst">{getattr_time / direct_time:<span class="hljs-number">.2</span>f}</span>x slower"</span>)
</code></pre>
<p>Running this benchmark shows significant performance differences:</p>
<pre><code class="lang-plaintext">Direct access: 0.027714 seconds
__getattr__ access: 0.060646 seconds
__getattr__ is 2.19x slower
</code></pre>
<p>As you can see, using <code>__getattr__</code> is more than twice as slow as direct attribute access. This might not matter for occasionally accessed attributes, but it can become significant in performance-critical code that accesses attributes in tight loops.</p>
<h3 id="heading-optimization-strategies"><strong>Optimization Strategies</strong></h3>
<p>Fortunately, there are various ways you can optimize magic methods.</p>
<ol>
<li><p><strong>Use slots for memory efficiency</strong>: This reduces memory usage and improves attribute access speed. It’s best for classes with many instances.</p>
</li>
<li><p><strong>Cache computed values</strong>: You can store results of expensive operations and update the cache only when necessary. Use <code>@property</code> for computed attributes.</p>
</li>
<li><p><strong>Minimize method calls</strong>: Make sure you avoid unnecessary magic method calls and use direct attribute access when possible. Consider using <code>__slots__</code> for frequently accessed attributes.</p>
</li>
</ol>
<h2 id="heading-best-practices"><strong>Best Practices</strong></h2>
<p>When using magic methods, follow these best practices to ensure your code is maintainable, efficient, and reliable.</p>
<h3 id="heading-1-be-consistent"><strong>1. Be Consistent</strong></h3>
<p>When implementing related magic methods, maintain consistency in behavior:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> functools <span class="hljs-keyword">import</span> total_ordering

<span class="hljs-meta">@total_ordering</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConsistentNumber</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, value</span>):</span>
        self.value = value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__eq__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, ConsistentNumber):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">return</span> self.value == other.value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__lt__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, ConsistentNumber):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-keyword">return</span> self.value &lt; other.value
</code></pre>
<h3 id="heading-2-return-notimplemented"><strong>2. Return NotImplemented</strong></h3>
<p>When an operation doesn't make sense, return <code>NotImplemented</code> to let Python try the reverse operation:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Money</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__add__</span>(<span class="hljs-params">self, other</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(other, Money):
            <span class="hljs-keyword">return</span> <span class="hljs-built_in">NotImplemented</span>
        <span class="hljs-comment"># ... rest of the implementation</span>
</code></pre>
<h3 id="heading-3-keep-it-simple"><strong>3. Keep It Simple</strong></h3>
<p>Magic methods should be simple and predictable. Avoid complex logic that could lead to unexpected behavior:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Good: Simple and predictable</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SimpleContainer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.items = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, index</span>):</span>
        <span class="hljs-keyword">return</span> self.items[index]

<span class="hljs-comment"># Bad: Complex and potentially confusing</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ComplexContainer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.items = []
        self.access_count = <span class="hljs-number">0</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, index</span>):</span>
        self.access_count += <span class="hljs-number">1</span>
        <span class="hljs-keyword">if</span> self.access_count &gt; <span class="hljs-number">100</span>:
            <span class="hljs-keyword">raise</span> RuntimeError(<span class="hljs-string">"Too many accesses"</span>)
        <span class="hljs-keyword">return</span> self.items[index]
</code></pre>
<h3 id="heading-4-document-behavior"><strong>4. Document Behavior</strong></h3>
<p>Clearly document how your magic methods behave, especially if they deviate from standard expectations:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CustomDict</span>(<span class="hljs-params">dict</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__missing__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-string">"""
        Called when a key is not found in the dictionary.
        Creates a new list for the key and returns it.
        This allows for automatic list creation when accessing
        non-existent keys.
        """</span>
        self[key] = []
        <span class="hljs-keyword">return</span> self[key]
</code></pre>
<h3 id="heading-5-consider-performance"><strong>5. Consider Performance</strong></h3>
<p>Be aware of the performance implications, especially for frequently called methods:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OptimizedContainer</span>:</span>
    __slots__ = [<span class="hljs-string">'items'</span>]  <span class="hljs-comment"># Use __slots__ for better performance</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.items = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, index</span>):</span>
        <span class="hljs-keyword">return</span> self.items[index]  <span class="hljs-comment"># Direct access is faster</span>
</code></pre>
<h3 id="heading-6-handle-edge-cases"><strong>6. Handle Edge Cases</strong></h3>
<p>Always consider edge cases and handle them appropriately:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SafeContainer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__getitem__</span>(<span class="hljs-params">self, key</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> isinstance(key, (int, slice)):
            <span class="hljs-keyword">raise</span> TypeError(<span class="hljs-string">"Index must be integer or slice"</span>)
        <span class="hljs-keyword">if</span> key &lt; <span class="hljs-number">0</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Index cannot be negative"</span>)
        <span class="hljs-comment"># ... rest of the implementation</span>
</code></pre>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>Python's magic methods provide a powerful way to make your classes behave like built-in types, enabling more intuitive and expressive code. Throughout this guide, we've explored how these methods work and how to use them effectively.</p>
<h3 id="heading-key-takeaways"><strong>Key Takeaways</strong></h3>
<ol>
<li><p><strong>Object representation</strong>:</p>
<ul>
<li><p>Use <code>__str__</code> for user-friendly output</p>
</li>
<li><p>Use <code>__repr__</code> for debugging and development</p>
</li>
</ul>
</li>
<li><p><strong>Operator overloading</strong>:</p>
<ul>
<li><p>Implement arithmetic and comparison operators</p>
</li>
<li><p>Return <code>NotImplemented</code> for unsupported operations</p>
</li>
<li><p>Use <code>@total_ordering</code> for consistent comparisons</p>
</li>
</ul>
</li>
<li><p><strong>Container behavior</strong>:</p>
<ul>
<li><p>Implement sequence and mapping protocols</p>
</li>
<li><p>Consider performance for frequently used operations</p>
</li>
<li><p>Handle edge cases appropriately</p>
</li>
</ul>
</li>
<li><p><strong>Resource management</strong>:</p>
<ul>
<li><p>Use context managers for proper resource handling</p>
</li>
<li><p>Implement <code>__enter__</code> and <code>__exit__</code> for cleanup</p>
</li>
<li><p>Handle exceptions in <code>__exit__</code></p>
</li>
</ul>
</li>
<li><p><strong>Performance optimization</strong>:</p>
<ul>
<li><p>Use <code>__slots__</code> for memory efficiency</p>
</li>
<li><p>Cache computed values when appropriate</p>
</li>
<li><p>Minimize method calls in frequently used code</p>
</li>
</ul>
</li>
</ol>
<h3 id="heading-when-to-use-magic-methods"><strong>When to Use Magic Methods</strong></h3>
<p>Magic methods are most useful when you need to:</p>
<ol>
<li><p>Create custom data structures</p>
</li>
<li><p>Implement domain-specific types</p>
</li>
<li><p>Manage resources properly</p>
</li>
<li><p>Add special behavior to your classes</p>
</li>
<li><p>Make your code more Pythonic</p>
</li>
</ol>
<h3 id="heading-when-to-avoid-magic-methods"><strong>When to Avoid Magic Methods</strong></h3>
<p>Avoid magic methods when:</p>
<ol>
<li><p>Simple attribute access is sufficient</p>
</li>
<li><p>The behavior would be confusing or unexpected</p>
</li>
<li><p>Performance is critical and magic methods would add overhead</p>
</li>
<li><p>The implementation would be overly complex</p>
</li>
</ol>
<p>Remember that with great power comes great responsibility. Use magic methods judiciously, keeping in mind their performance implications and the principle of least surprise. When used appropriately, magic methods can significantly enhance the readability and expressiveness of your code.</p>
<h2 id="heading-references-and-further-reading"><strong>References and Further Reading</strong></h2>
<h3 id="heading-official-python-documentation"><strong>Official Python Documentation</strong></h3>
<ol>
<li><p><a target="_blank" href="https://docs.python.org/3/reference/datamodel.html">Python Data Model - Official Documentation</a> - Comprehensive guide to Python's data model and magic methods.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/functools.html#functools.total_ordering">functools.total_ordering</a> - Documentation for the total_ordering decorator that automatically fills in missing comparison methods.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/reference/lexical_analysis.html#reserved-classes-of-identifiers">Python Special Method Names</a> - Official reference for special method identifiers in Python.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/collections.abc.html">Collections Abstract Base Classes</a> - Learn about abstract base classes for containers which define the interfaces that your container classes can implement.</p>
</li>
</ol>
<h3 id="heading-community-resources"><strong>Community Resources</strong></h3>
<ol start="5">
<li><a target="_blank" href="https://rszalski.github.io/magicmethods/">A Guide to Python's Magic Methods - Rafe Kettler</a> - Practical examples of magic methods and common use cases.</li>
</ol>
<h3 id="heading-further-reading"><strong>Further Reading</strong></h3>
<p>If you enjoyed this article, you might find these Python-related articles on my <a target="_blank" href="https://wewake.dev">personal blog</a> useful:</p>
<ol>
<li><p><a target="_blank" href="https://wewake.dev/posts/practical-experiments-for-django-orm-query-optimizations/">Practical Experiments for Django ORM Query Optimizations</a> - Learn how to optimize your Django ORM queries with practical examples and experiments.</p>
</li>
<li><p><a target="_blank" href="https://wewake.dev/posts/high-cost-of-sync-uwsgi/">The High Cost of Synchronous uWSGI</a> - Understand the performance implications of synchronous processing in uWSGI and how it affects your Python web applications.</p>
</li>
</ol>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
