<?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[ Vivek Sahu - 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[ Vivek Sahu - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 22:24:01 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/viv1/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Use Python's Built-in Profiling Tools: Examples and Best Practices ]]>
                </title>
                <description>
                    <![CDATA[ Python is known for its simplicity and readability, making it a favorite among developers. But this simplicity sometimes comes at the cost of performance. When your Python application grows or needs to handle larger workloads, understanding what's ha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-pythons-built-in-profiling-tools-examples-and-best-practices/</link>
                <guid isPermaLink="false">67e2d5a9436e3bed610a8a95</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ performance ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Performance Optimization ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python advanced ]]>
                    </category>
                
                    <category>
                        <![CDATA[ code analysis ]]>
                    </category>
                
                    <category>
                        <![CDATA[ code profiling ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vivek Sahu ]]>
                </dc:creator>
                <pubDate>Tue, 25 Mar 2025 16:11:21 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742917060232/7ea623ac-4c4d-4bb9-9edf-f9041a8bc9ae.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p><code>Python</code> is known for its simplicity and readability, making it a favorite among developers. But this simplicity sometimes comes at the cost of performance. When your Python application grows or needs to handle larger workloads, understanding what's happening under the hood becomes crucial.</p>
<p>While many developers reach for third-party profiling tools, Python's standard library already comes packed with powerful profiling capabilities that are often overlooked or underutilized.</p>
<p>In this article, you'll learn how to use these built-in profiling tools beyond their basic usage. You'll discover how to combine and leverage them to gain deep insights into your code's performance without installing additional packages.</p>
<h2 id="heading-table-of-contents"><strong>Table of Contents</strong></h2>
<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-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Prerequi</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">site</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">s</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-22-python-built-in-profiling-tools-beyond-basics.md#introduction">The Bui</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">l</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">t-in</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Profiling A</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">r</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">senal</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-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">The</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal"><code>timeit</code> M</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">odule</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-22-python-built-in-profiling-tools-beyond-basics.md#introduction">The</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal"></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites"><code>cProfile</code></a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Module</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-22-python-built-in-profiling-tools-beyond-basics.md#introduction">T</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">h</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">e <code>pstats</code> M</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">odule</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-22-python-built-in-profiling-tools-beyond-basics.md#introduction">The</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal"></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites"><code>profile</code></a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Module</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-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">P</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ractical Ex</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">periments</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-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">Setup</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-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">Exp</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">eriment 1: Basic vs A</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">dvanced <code>timeit</code></a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">Usage</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-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">Experiment 2</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">: Effective</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module"><code>c</code></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites"><code>Profile</code> Analy</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">sis</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-22-python-built-in-profiling-tools-beyond-basics.md#the-cprofile-module">Experiment</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">3</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">: Combining</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">T</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ools for Real</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">-world</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-profile-module">P</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-cprofile-module">rofiling</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-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">Best Practic</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">es</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-22-python-built-in-profiling-tools-beyond-basics.md#introduction">Conclu</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-timeit-module">s</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ion</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-22-python-built-in-profiling-tools-beyond-basics.md#introduction">References</a> <a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-cprofile-module"></a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">a</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#introduction">nd Further R</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#the-built-in-profiling-arsenal">e</a><a target="_blank" href="https://file+.vscode-resource.vscode-cdn.net/Users/viv1/Documents/workspace/BLOG/BLOG/viv1.github.io/_posts/2025-02-22-python-built-in-profiling-tools-beyond-basics.md#prerequisites">ading</a></p>
</li>
</ul>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before diving into the profiling techniques, make sure you have:</p>
<ol>
<li><p><strong>Python 3.6+</strong>: All examples in this article are compatible with Python 3.6 and newer versions.</p>
</li>
<li><p><strong>Basic Python Knowledge</strong>: You should be comfortable with Python fundamentals like functions, modules, and basic data structures.</p>
</li>
<li><p><strong>A Test Environment</strong>: Either a local Python environment or a virtual environment where you can run the code examples.</p>
</li>
</ol>
<p>No external libraries are required for this tutorial as we'll be focusing exclusively on Python's built-in profiling tools. You can verify your Python version this way:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Verify your Python version</span>
<span class="hljs-keyword">import</span> sys
print(<span class="hljs-string">f"Python version: <span class="hljs-subst">{sys.version}</span>"</span>)
</code></pre>
<h2 id="heading-the-built-in-profiling-arsenal"><strong>The Built-in Profiling Arsenal</strong></h2>
<p>Python ships with several profiling tools in its standard library. Let's explore each one and understand their various strengths.</p>
<h3 id="heading-the-timeit-module"><strong>The</strong> <code>timeit</code> Module</h3>
<p>Most Python developers are familiar with the basic <code>timeit</code> usage:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> timeit

<span class="hljs-comment"># Basic usage</span>
execution_time = timeit.timeit(<span class="hljs-string">'"-".join(str(n) for n in range(100))'</span>, number=<span class="hljs-number">1000</span>)
print(<span class="hljs-string">f"Execution time: <span class="hljs-subst">{execution_time}</span> seconds"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Execution time: 0.006027 seconds</span>
</code></pre>
<p>This basic example measures how long it takes to join 100 numbers into a string with hyphens. The <code>number=1000</code> parameter tells Python to run this operation 1,000 times and return the total execution time, which helps average out any random fluctuations.</p>
<p>However, <code>timeit</code> offers much more flexibility than most developers realize. Let's explore some powerful ways to use it:</p>
<p><strong>Setup Code Separation</strong>:</p>
<pre><code class="lang-python">setup_code = <span class="hljs-string">"""
data = [i for i in range(1000)]
"""</span>

test_code = <span class="hljs-string">"""
result = [x * 2 for x in data]
"""</span>

execution_time = timeit.timeit(stmt=test_code, setup=setup_code, number=<span class="hljs-number">100</span>)
print(<span class="hljs-string">f"Execution time: <span class="hljs-subst">{execution_time}</span> seconds"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Execution time: 0.001420 seconds</span>
</code></pre>
<p>In this example, we separate the setup code from the code being timed. This is extremely useful when:</p>
<ul>
<li><p>You need to create test data but don't want that time included in your measurement</p>
</li>
<li><p>You're timing a function that relies on imports or variable definitions</p>
</li>
<li><p>You want to reuse the same setup for multiple timing tests</p>
</li>
</ul>
<p>The advantage is that only the code in <code>test_code</code> is timed, while the setup runs just once before the timing begins.</p>
<p><strong>Comparing Functions</strong>:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">approach_1</span>(<span class="hljs-params">data</span>):</span>
    <span class="hljs-keyword">return</span> [x * <span class="hljs-number">2</span> <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> data]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">approach_2</span>(<span class="hljs-params">data</span>):</span>
    <span class="hljs-keyword">return</span> list(map(<span class="hljs-keyword">lambda</span> x: x * <span class="hljs-number">2</span>, data))

data = list(range(<span class="hljs-number">1000</span>))

time1 = timeit.timeit(<span class="hljs-keyword">lambda</span>: approach_1(data), number=<span class="hljs-number">100</span>)
time2 = timeit.timeit(<span class="hljs-keyword">lambda</span>: approach_2(data), number=<span class="hljs-number">100</span>)

print(<span class="hljs-string">f"Approach 1: <span class="hljs-subst">{time1}</span> seconds"</span>)
print(<span class="hljs-string">f"Approach 2: <span class="hljs-subst">{time2}</span> seconds"</span>)
print(<span class="hljs-string">f"Ratio: <span class="hljs-subst">{time2/time1:<span class="hljs-number">.2</span>f}</span>x"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Approach 1: 0.001406 seconds</span>
<span class="hljs-comment"># Approach 2: 0.003049 seconds</span>
<span class="hljs-comment"># Ratio: 2.17x</span>
</code></pre>
<p>This example demonstrates how to compare two different implementations of the same functionality. Here we're comparing:</p>
<ol>
<li><p>A list comprehension approach</p>
</li>
<li><p>A <code>map()</code> with lambda approach</p>
</li>
</ol>
<p>By using lambda functions, we can pass existing data to our functions when timing them. This directly measures real-world scenarios where your functions are working with existing data. The ratio calculation makes it easy to understand exactly how much faster one approach is than the other.</p>
<p>In this case, we can see the list comprehension is about 2.17 times faster than the map approach for this specific operation.</p>
<h3 id="heading-the-cprofile-module"><strong>The</strong> <code>cProfile</code> Module</h3>
<p><code>cProfile</code> is Python's C-based profiler that provides detailed statistics about function calls. Many developers use it with its default settings:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_function</span>():</span>
    total = <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">100000</span>):  <span class="hljs-comment"># Reduced for faster execution</span>
        total += i
    <span class="hljs-keyword">return</span> total

cProfile.run(<span class="hljs-string">'my_function()'</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment">#          4 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: standard name</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 &lt;string&gt;:1(&lt;module&gt;)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 {built-in method builtins.exec}</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>This basic example runs the profiler on a simple function that sums the numbers from 0 to 99,999. The output provides several key pieces of information:</p>
<ul>
<li><p><code>ncalls</code>: How many times each function was called</p>
</li>
<li><p><code>tottime</code>: The total time spent in the function (excluding time in subfunctions)</p>
</li>
<li><p><code>percall</code>: Time per call (<code>tottime</code> divided by <code>ncalls</code>)</p>
</li>
<li><p><code>cumtime</code>: Cumulative time spent in this function and all subfunctions</p>
</li>
<li><p><code>filename:lineno(function)</code>: Where the function is defined</p>
</li>
</ul>
<p>This gives you a comprehensive view of where time is being spent in your code, but there's much more you can do with <code>cProfile</code>.</p>
<p>The real power comes from advanced usage techniques:</p>
<p><strong>Sorting Results</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats

<span class="hljs-comment"># Profile the function</span>
profiler = cProfile.Profile()
profiler.enable()
my_function()
profiler.disable()

<span class="hljs-comment"># Create stats object</span>
stats = pstats.Stats(profiler)

<span class="hljs-comment"># Sort by different metrics</span>
stats.sort_stats(<span class="hljs-string">'cumulative'</span>).print_stats(<span class="hljs-number">10</span>)  <span class="hljs-comment"># Top 10 functions by cumulative time</span>
stats.sort_stats(<span class="hljs-string">'calls'</span>).print_stats(<span class="hljs-number">10</span>)       <span class="hljs-comment"># Top 10 functions by call count</span>
stats.sort_stats(<span class="hljs-string">'time'</span>).print_stats(<span class="hljs-number">10</span>)        <span class="hljs-comment"># Top 10 functions by time</span>

<span class="hljs-comment"># Sample output for cumulative sorting:</span>
<span class="hljs-comment">#          2 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: cumulative time</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>This example demonstrates how to control the profiling process and sort the results in different ways. The advantages are:</p>
<ol>
<li><p>You can enable/disable profiling around specific sections of code</p>
</li>
<li><p>You can sort results by different metrics to identify different types of bottlenecks:</p>
<ul>
<li><p><code>cumulative</code>: Find functions that consume the most time overall (including subfunctions)</p>
</li>
<li><p><code>calls</code>: Find functions called most frequently</p>
</li>
<li><p><code>time</code>: Find functions with the highest self-time (excluding subfunctions)</p>
</li>
</ul>
</li>
<li><p>Limit output to only the top N results with <code>print_stats(N)</code></p>
</li>
</ol>
<p>This flexibility lets you focus on specific performance aspects of your code.</p>
<p><strong>Filtering Results</strong>:</p>
<pre><code class="lang-python">stats.strip_dirs().print_stats()  <span class="hljs-comment"># Remove directory paths for cleaner output</span>
stats.print_stats(<span class="hljs-string">'my_module'</span>)   <span class="hljs-comment"># Only show results from my_module</span>

<span class="hljs-comment"># Sample output with strip_dirs():</span>
<span class="hljs-comment">#          2 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Random listing order was used</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>These filtering techniques are invaluable when working with larger applications:</p>
<ul>
<li><p><code>strip_dirs()</code> removes directory paths, making the output much more readable</p>
</li>
<li><p><code>print_stats('my_module')</code> filters results to only show functions from a specific module, letting you focus on your code rather than library code</p>
</li>
</ul>
<p>This is particularly useful when profiling large applications where the full output might include hundreds or thousands of function calls.</p>
<h3 id="heading-the-pstats-module"><strong>The</strong> <code>pstats</code> Module</h3>
<p>The <code>pstats</code> module is often overlooked but provides powerful ways to analyze profiling data:</p>
<p><strong>Saving and Loading Profile Data</strong>:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats

<span class="hljs-comment"># Save profile data to a file</span>
cProfile.run(<span class="hljs-string">'my_function()'</span>, <span class="hljs-string">'my_profile.stats'</span>)

<span class="hljs-comment"># Load and analyze later</span>
stats = pstats.Stats(<span class="hljs-string">'my_profile.stats'</span>)
stats.strip_dirs().sort_stats(<span class="hljs-string">'cumulative'</span>).print_stats(<span class="hljs-number">10</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Wed Mar 20 14:30:00 2024    my_profile.stats</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#          4 function calls in 0.002 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: cumulative time</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 {built-in method builtins.exec}</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 &lt;string&gt;:1(&lt;module&gt;)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}</span>
</code></pre>
<p>This example shows how to save profiling data to a file and load it later for analysis. The key advantages are:</p>
<ol>
<li><p>You can collect profiling data in one session or environment and analyze it in another</p>
</li>
<li><p>You can share profiling data with team members without them needing to run the code</p>
</li>
<li><p>You can save profiling data from production environments where interactive analysis might not be possible</p>
</li>
<li><p>You can compare different runs over time to track performance improvements</p>
</li>
</ol>
<p>This approach separates data collection from analysis, making it more flexible for real-world applications.</p>
<p><strong>Combining Multiple Profiles</strong>:</p>
<pre><code class="lang-python">stats = pstats.Stats(<span class="hljs-string">'profile1.stats'</span>)
stats.add(<span class="hljs-string">'profile2.stats'</span>)
stats.add(<span class="hljs-string">'profile3.stats'</span>)
stats.sort_stats(<span class="hljs-string">'time'</span>).print_stats()

<span class="hljs-comment"># This allows you to combine results from multiple profiling runs,</span>
<span class="hljs-comment"># useful for aggregating data from different test cases or scenarios</span>
</code></pre>
<p>This powerful feature lets you combine results from multiple profiling runs. This is useful for:</p>
<ol>
<li><p>Comparing performance across different inputs</p>
</li>
<li><p>Aggregating data from multiple test scenarios</p>
</li>
<li><p>Combining data from different parts of your application</p>
</li>
<li><p>Building a more comprehensive performance picture across multiple runs</p>
</li>
</ol>
<p>By combining stats from multiple runs, you can identify patterns that might not be apparent from a single profiling session.</p>
<h3 id="heading-the-profile-module"><strong>The</strong> <code>profile</code> Module</h3>
<p>The <code>profile</code> module is a pure Python implementation of the profiler interface. While it's slower than <code>cProfile</code>, it can be more flexible for specific cases:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> profile

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">my_function</span>():</span>
    total = <span class="hljs-number">0</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">100000</span>):  <span class="hljs-comment"># Using 100000 for faster execution</span>
        total += i
    <span class="hljs-keyword">return</span> total

profile.run(<span class="hljs-string">'my_function()'</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment">#          5 function calls in 0.011 seconds</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    Ordered by: standard name</span>
<span class="hljs-comment">#</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 :0(exec)</span>
<span class="hljs-comment">#         1    0.009    0.009    0.009    0.009 :0(setprofile)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.002    0.002 &lt;string&gt;:1(&lt;module&gt;)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.011    0.011 profile:0(my_function())</span>
<span class="hljs-comment">#         0    0.000             0.000          profile:0(profiler)</span>
<span class="hljs-comment">#         1    0.002    0.002    0.002    0.002 &lt;stdin&gt;:1(my_function)</span>
</code></pre>
<p>The <code>profile</code> module works similarly to <code>cProfile</code> but offers advantages in specific scenarios:</p>
<ol>
<li><p>It's implemented in pure Python, making it easier to modify if you need custom profiling behavior</p>
</li>
<li><p>You can subclass and extend it to implement custom profiling logic</p>
</li>
<li><p>It allows more fine-grained control over the profiling process</p>
</li>
<li><p>It's useful for profiling in environments where the C extension might not be available</p>
</li>
</ol>
<p>While it's slower than <code>cProfile</code> (because it's implemented in Python rather than C), its flexibility makes it valuable for specialized profiling needs.</p>
<p>The <code>profile</code> module follows the same API as <code>cProfile</code>, so you can use all the same techniques for analyzing results.</p>
<h2 id="heading-practical-experiments"><strong>Practical Experiments</strong></h2>
<p>Let's put these tools to practical use with some experiments.</p>
<h3 id="heading-setup"><strong>Setup</strong></h3>
<p>First, create a simple Python module with various functions to profile:</p>
<pre><code class="lang-python"><span class="hljs-comment"># profiling_example.py</span>

<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">import</span> random

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_data</span>(<span class="hljs-params">data</span>):</span>
    result = []
    <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> data:
        result.append(process_item(item))
    <span class="hljs-keyword">return</span> result

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_item</span>(<span class="hljs-params">item</span>):</span>
    <span class="hljs-comment"># Simulate processing time</span>
    time.sleep(<span class="hljs-number">0.0001</span>)  <span class="hljs-comment"># Small delay for demonstration purposes</span>
    <span class="hljs-keyword">return</span> item * <span class="hljs-number">2</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_data</span>(<span class="hljs-params">size</span>):</span>
    <span class="hljs-keyword">return</span> [random.randint(<span class="hljs-number">1</span>, <span class="hljs-number">100</span>) <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(size)]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_data_optimized</span>(<span class="hljs-params">data</span>):</span>
    <span class="hljs-keyword">return</span> [process_item(item) <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> data]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    data = generate_data(<span class="hljs-number">50</span>)
    result1 = process_data(data)
    result2 = process_data_optimized(data)
    <span class="hljs-keyword">assert</span> result1 == result2
    <span class="hljs-keyword">return</span> result1

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    main()
</code></pre>
<h3 id="heading-experiment-1-basic-vs-advanced-timeit-usage"><strong>Experiment 1: Basic vs Advanced</strong> <code>timeit</code> Usage</h3>
<p>Let's compare different ways of timing our functions:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> timeit
<span class="hljs-keyword">from</span> profiling_example <span class="hljs-keyword">import</span> generate_data, process_data, process_data_optimized

<span class="hljs-comment"># Method 1: Basic string evaluation (limited but simple)</span>
setup1 = <span class="hljs-string">"""
from profiling_example import generate_data, process_data
data = generate_data(5)  # Using a small size for demonstration
"""</span>
basic_time = timeit.timeit(<span class="hljs-string">'process_data(data)'</span>, setup=setup1, number=<span class="hljs-number">5</span>)
print(<span class="hljs-string">f"Basic timing: <span class="hljs-subst">{basic_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)

<span class="hljs-comment"># Method 2: Using lambda for better control</span>
data = generate_data(<span class="hljs-number">5</span>)
advanced_time = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data(data), number=<span class="hljs-number">5</span>)
print(<span class="hljs-string">f"Advanced timing: <span class="hljs-subst">{advanced_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)

<span class="hljs-comment"># Method 3: Comparing implementations</span>
data = generate_data(<span class="hljs-number">5</span>)
original_time = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data(data), number=<span class="hljs-number">5</span>)
optimized_time = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data_optimized(data), number=<span class="hljs-number">5</span>)
print(<span class="hljs-string">f"Original implementation: <span class="hljs-subst">{original_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"Optimized implementation: <span class="hljs-subst">{optimized_time:<span class="hljs-number">.4</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"Improvement ratio: <span class="hljs-subst">{original_time/optimized_time:<span class="hljs-number">.2</span>f}</span>x"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Basic timing: 0.0032 seconds</span>
<span class="hljs-comment"># Advanced timing: 0.0034 seconds</span>
<span class="hljs-comment"># Original implementation: 0.0033 seconds</span>
<span class="hljs-comment"># Optimized implementation: 0.0034 seconds</span>
<span class="hljs-comment"># Improvement ratio: 0.98x</span>
</code></pre>
<p>This experiment demonstrates three different approaches to timing code with the <code>timeit</code> module:</p>
<p><strong>Method 1: Basic string evaluation</strong> – This approach evaluates a string of code after running the setup code. The advantages include:</p>
<ul>
<li><p>Simple syntax for basic timing needs</p>
</li>
<li><p>Setup code runs only once, not during each timing run</p>
</li>
<li><p>Good for timing simple expressions</p>
</li>
</ul>
<p><strong>Method 2: Lambda functions</strong> – This more advanced approach uses lambda functions to call our functions directly. Benefits include:</p>
<ul>
<li><p>Direct access to functions and variables in the current scope</p>
</li>
<li><p>No need to import functions in setup code</p>
</li>
<li><p>Better for timing functions that take arguments</p>
</li>
<li><p>More intuitive for complex timing scenarios</p>
</li>
</ul>
<p><strong>Method 3: Implementation comparison</strong> – This practical approach compares two different implementations of the same functionality. This is valuable when:</p>
<ul>
<li><p>Deciding between alternative implementations</p>
</li>
<li><p>Measuring the impact of optimizations</p>
</li>
<li><p>Quantifying performance differences with a ratio</p>
</li>
</ul>
<p>In this example, the list comprehension isn't significantly faster because the dominant cost is the <code>time.sleep()</code> call in both implementations. In real-world cases with actual computation instead of sleep, the difference is often more pronounced.</p>
<h3 id="heading-experiment-2-effective-cprofile-analysis"><strong>Experiment 2: Effective</strong> <code>cProfile</code> Analysis</h3>
<p>Now let's use <code>cProfile</code> to identify bottlenecks:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats
<span class="hljs-keyword">import</span> io
<span class="hljs-keyword">from</span> profiling_example <span class="hljs-keyword">import</span> main

<span class="hljs-comment"># Method 1: Basic profiling</span>
cProfile.run(<span class="hljs-string">'main()'</span>)

<span class="hljs-comment"># Sample output snippet:</span>
<span class="hljs-comment">#         679 function calls in 0.014 seconds</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#      100    0.000    0.000    0.014    0.000 profiling_example.py:10(process_item)</span>
<span class="hljs-comment">#      100    0.014    0.000    0.014    0.000 {built-in method time.sleep}</span>
<span class="hljs-comment">#        1    0.000    0.000    0.007    0.007 profiling_example.py:4(process_data)</span>
<span class="hljs-comment">#        1    0.000    0.000    0.007    0.007 profiling_example.py:18(process_data_optimized)</span>

<span class="hljs-comment"># Method 2: Capturing and analyzing results</span>
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()

<span class="hljs-comment"># Redirect output to string for analysis</span>
s = io.StringIO()
stats = pstats.Stats(profiler, stream=s).sort_stats(<span class="hljs-string">'cumulative'</span>)
stats.print_stats(<span class="hljs-number">10</span>)  <span class="hljs-comment"># Print top 10 functions by cumulative time</span>
print(s.getvalue())

<span class="hljs-comment"># Method 3: Focus on specific functions</span>
s = io.StringIO()
stats = pstats.Stats(profiler, stream=s).sort_stats(<span class="hljs-string">'cumulative'</span>)
stats.print_callers(<span class="hljs-string">'process_item'</span>)  <span class="hljs-comment"># Show what's calling this function</span>
print(s.getvalue())

<span class="hljs-comment"># Sample output for the callers analysis:</span>
<span class="hljs-comment"># Function was called by...</span>
<span class="hljs-comment">#       ncalls  tottime  cumtime</span>
<span class="hljs-comment"># profiling_example.py:10(process_item)  &lt;-  </span>
<span class="hljs-comment">#     50    0.000    0.007  profiling_example.py:4(process_data)</span>
<span class="hljs-comment">#     50    0.000    0.007  profiling_example.py:18(process_data_optimized)</span>
</code></pre>
<p>This experiment demonstrates three powerful <code>cProfile</code> techniques for identifying bottlenecks:</p>
<p><strong>Method 1: Basic profiling</strong> – Using <code>cProfile.run()</code> to profile a function call provides an immediate overview of performance. This technique:</p>
<ul>
<li><p>Gives you a quick snapshot of all function calls</p>
</li>
<li><p>Shows precisely where time is being spent</p>
</li>
<li><p>Is easy to use with minimal setup</p>
</li>
<li><p>Identifies the most time-consuming operations</p>
</li>
</ul>
<p>In our example, we can immediately see that <code>time.sleep()</code> is consuming most of the execution time.</p>
<p><strong>Method 2: Programmatic profiling and analysis</strong> – This approach gives you more control:</p>
<ul>
<li><p>You can enable/disable profiling for specific sections of code</p>
</li>
<li><p>You can save the results to a variable for further analysis</p>
</li>
<li><p>You can customize how results are sorted and displayed</p>
</li>
<li><p>You can redirect output to a string or file for post-processing</p>
</li>
</ul>
<p>This method is particularly useful for profiling specific parts of a larger application.</p>
<p><strong>Method 3: Caller analysis</strong> – The <code>print_callers()</code> method is extremely valuable because it:</p>
<ul>
<li><p>Shows which functions are calling your bottleneck functions</p>
</li>
<li><p>Helps identify which code paths are contributing to performance issues</p>
</li>
<li><p>Reveals how many times each caller invokes a particular function</p>
</li>
<li><p>Provides context that's crucial for understanding performance patterns</p>
</li>
</ul>
<p>In our example, we can see that both <code>process_data</code> and <code>process_data_optimized</code> are calling <code>process_item</code> 50 times each, confirming they're contributing equally to the bottleneck.</p>
<p>This immediate shows us that <code>process_item</code> is the bottleneck, specifically the <code>time.sleep()</code> call inside it, and it's being called equally by both implementations.</p>
<h3 id="heading-experiment-3-combining-tools-for-real-world-profiling"><strong>Experiment 3: Combining Tools for Real-world Profiling</strong></h3>
<p>In real-world scenarios, combining profiling tools gives the most complete picture:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> cProfile
<span class="hljs-keyword">import</span> pstats
<span class="hljs-keyword">import</span> timeit
<span class="hljs-keyword">from</span> profiling_example <span class="hljs-keyword">import</span> main, process_data, process_data_optimized, generate_data

<span class="hljs-comment"># First, use timeit to get baseline performance of main components</span>
data = generate_data(<span class="hljs-number">50</span>)
time_process = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data(data), number=<span class="hljs-number">3</span>)
time_process_opt = timeit.timeit(<span class="hljs-keyword">lambda</span>: process_data_optimized(data), number=<span class="hljs-number">3</span>)
print(<span class="hljs-string">f"Process data: <span class="hljs-subst">{time_process:<span class="hljs-number">.4</span>f}</span>s"</span>)
print(<span class="hljs-string">f"Process data optimized: <span class="hljs-subst">{time_process_opt:<span class="hljs-number">.4</span>f}</span>s"</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Process data: 0.0196s</span>
<span class="hljs-comment"># Process data optimized: 0.0194s</span>

<span class="hljs-comment"># Then, use cProfile for deeper insights</span>
profiler = cProfile.Profile()
profiler.enable()
main()
profiler.disable()

<span class="hljs-comment"># Save stats for later analysis</span>
profiler.dump_stats(<span class="hljs-string">'profile_results.stats'</span>)

<span class="hljs-comment"># Load and analyze</span>
stats = pstats.Stats(<span class="hljs-string">'profile_results.stats'</span>)
stats.strip_dirs().sort_stats(<span class="hljs-string">'cumulative'</span>).print_stats(<span class="hljs-number">10</span>)

<span class="hljs-comment"># Sample output:</span>
<span class="hljs-comment"># Wed Mar 20 14:30:00 2024    profile_results.stats</span>
<span class="hljs-comment">#          659 function calls in 0.013 seconds</span>
<span class="hljs-comment">#    Ordered by: cumulative time</span>
<span class="hljs-comment">#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)</span>
<span class="hljs-comment">#         1    0.000    0.000    0.013    0.013 profiling_example.py:21(main)</span>
<span class="hljs-comment">#       100    0.000    0.000    0.013    0.000 profiling_example.py:10(process_item)</span>
<span class="hljs-comment">#       100    0.013    0.000    0.013    0.000 {built-in method time.sleep}</span>
</code></pre>
<p>This experiment demonstrates a comprehensive real-world profiling strategy that combines multiple tools:</p>
<p><strong>First step: High-level timing with</strong> <code>timeit</code> – We start with <code>timeit</code> to:</p>
<ul>
<li><p>Get baseline performance metrics for specific functions</p>
</li>
<li><p>Compare different implementations directly</p>
</li>
<li><p>Measure overall execution time</p>
</li>
<li><p>Identify which high-level components might need optimization</p>
</li>
</ul>
<p>This gives us a quick overview that both implementations take about the same time, confirming our earlier findings.</p>
<p><strong>Second step: Detailed profiling with</strong> <code>cProfile</code> – Next, we use <code>cProfile</code> to:</p>
<ul>
<li><p>Get a function-by-function breakdown of execution time</p>
</li>
<li><p>Identify specific bottlenecks at a granular level</p>
</li>
<li><p>See the number of calls to each function</p>
</li>
<li><p>Understand the call hierarchy</p>
</li>
</ul>
<p><strong>Third step: Saving and analyzing with</strong> <code>pstats</code> – Finally, we:</p>
<ul>
<li><p>Save profiling data to a file for persistence</p>
</li>
<li><p>Load the data and apply filtering/sorting</p>
</li>
<li><p>Focus on the most time-consuming functions</p>
</li>
<li><p>Get a clean, readable output</p>
</li>
</ul>
<p>This multi-tool approach provides several advantages:</p>
<ol>
<li><p>You get both high-level and detailed insights</p>
</li>
<li><p>You can save profiling data for later comparison</p>
</li>
<li><p>You can share results with team members</p>
</li>
<li><p>You can track performance changes over time</p>
</li>
</ol>
<p>In our example, we confirm that our main bottleneck is the <code>time.sleep()</code> call inside <code>process_item</code>, which accounts for most of the execution time. Without this combined approach, we might have missed important details or wasted time optimizing the wrong parts of our code.</p>
<p>This approach gives you both high-level timing information and detailed profiling data, allowing for a comprehensive performance analysis.</p>
<h2 id="heading-best-practices"><strong>Best Practices</strong></h2>
<p>Based on our experiments, here are some best practices for effective profiling:</p>
<ol>
<li><p><strong>Start with the right tool for the job</strong>:</p>
<ul>
<li><p>Use <code>timeit</code> for quick, targeted measurements of specific functions or code blocks</p>
</li>
<li><p>Use <code>cProfile</code> for comprehensive program analysis when you need to understand how all parts of your code interact</p>
</li>
<li><p>Use <code>pstats</code> for in-depth analysis of profiling data when you need to filter, sort, and interpret complex profiling results</p>
</li>
</ul>
</li>
</ol>
<p>    For example, if you're just trying to decide between two implementations of a sorting algorithm, <code>timeit</code> is sufficient. But if you're trying to understand why your entire web application is slow, start with <code>cProfile</code>.</p>
<ol start="2">
<li><p><strong>Profile realistic workloads</strong>:</p>
<ul>
<li><p>Synthetic benchmarks often mislead because they don't reflect real-world usage patterns</p>
</li>
<li><p>Use production-like data sizes to see how your code scales with realistic inputs</p>
</li>
<li><p>Run multiple iterations to account for variance and ensure your results are reliable</p>
</li>
</ul>
</li>
</ol>
<p>    A function that's fast with 10 items might be painfully slow with 10,000. Always test with data sizes that match your production needs.</p>
<ol start="3">
<li><p><strong>Focus on the right metrics</strong>:</p>
<ul>
<li><p><code>cumulative</code> time shows the total time spent in a function and all its calls. It’s useful for finding the overall most expensive operations.</p>
</li>
<li><p><code>tottime</code> shows time spent only in the function itself. It’s useful for finding inefficient implementations.</p>
</li>
<li><p><code>ncalls</code> helps identify functions called excessively. It’s useful for finding redundant operations.</p>
</li>
</ul>
</li>
</ol>
<p>    For example, a function with a small <code>tottime</code> but large <code>cumulative</code> time might be efficient itself but is calling expensive subfunctions.</p>
<ol start="4">
<li><p><strong>Save profiling data for comparison</strong>:</p>
<ul>
<li><p>Use <code>profiler.dump_stats()</code> to save data from different versions of your code</p>
</li>
<li><p>Compare before and after optimization to quantify improvements</p>
</li>
<li><p>Track performance over time to catch regressions early</p>
</li>
</ul>
</li>
</ol>
<p>    This practice helps you prove that your optimizations are actually working and prevents performance from degrading over time.</p>
<ol start="5">
<li><p><strong>Look for the 80/20 rule</strong>:</p>
<ul>
<li><p>80% of time is often spent in 20% of the code. Focus optimization efforts on these "hot spots"</p>
</li>
<li><p>Focus optimization efforts on the functions with the highest cumulative time.</p>
</li>
<li><p>Don't optimize what isn't slow – premature optimization wastes time and can make code more complex.</p>
</li>
</ul>
</li>
</ol>
<p>    For example, in our experiments, the <code>time.sleep()</code> call was the clear bottleneck. Optimizing anything else would be pointless until that's addressed.</p>
<p>By following these practices, you'll make the most efficient use of your profiling tools and focus your optimization efforts where they'll have the greatest impact.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Python's built-in profiling tools offer a powerful arsenal for identifying and resolving performance bottlenecks in your code. By leveraging the <code>timeit</code>, <code>cProfile</code>, and <code>pstats</code> modules effectively, you can get deep insights into your application's performance without relying on third-party tools.</p>
<p>Each tool serves a specific purpose:</p>
<ul>
<li><p><code>timeit</code> helps you measure execution time of specific code snippets</p>
</li>
<li><p><code>cProfile</code> gives you a comprehensive view of function calls and execution time</p>
</li>
<li><p><code>pstats</code> lets you analyze, filter, and interpret profiling data</p>
</li>
<li><p><code>profile</code> provides a customizable profiling interface for special cases</p>
</li>
</ul>
<p>The greatest power comes from combining these tools, as we demonstrated in our practical experiments. This allows you to approach performance optimization systematically:</p>
<ol>
<li><p>Identify high-level performance concerns with <code>timeit</code></p>
</li>
<li><p>Drill down into specific bottlenecks with <code>cProfile</code></p>
</li>
<li><p>Analyze and interpret results with <code>pstats</code></p>
</li>
<li><p>Make targeted optimizations based on data, not guesswork</p>
</li>
</ol>
<p>Remember that profiling is as much an art as it is a science. The goal isn't just to make code faster, but to understand why it's slow in the first place. With the techniques demonstrated in this article, you're well-equipped to tackle performance challenges in your Python applications.</p>
<p>Apply these profiling techniques to your own code, and you'll be surprised at what you discover. Often, the bottlenecks aren't where you expect them to be!</p>
<h2 id="heading-references-and-further-reading"><strong>References and Further Reading</strong></h2>
<ul>
<li><p><a target="_blank" href="https://docs.python.org/3/library/timeit.html">Python <code>timeit</code> documentation</a>: The official documentation for the <code>timeit</code> module, with detailed explanations of all parameters and functions.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html">Python <code>cProfile</code> documentation</a>: Comprehensive guide to profiling modules, including both <code>cProfile</code> and <code>profile</code>.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html#pstats.Stats">Python <code>pstats</code> documentation</a>: Detailed reference for the <code>pstats</code> module, explaining all methods for analyzing profiling data.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html">Python Profilers - Official Documentation</a>: The complete official documentation on Python's profiling capabilities.</p>
</li>
<li><p><a target="_blank" href="https://wiki.python.org/moin/PythonSpeed/PerformanceTips">Python Speed - Performance Tips</a>: A collection of practical tips for optimizing Python code once you've identified bottlenecks.</p>
</li>
<li><p><a target="_blank" href="https://docs.python.org/3/library/profile.html#module-cProfile">The Python Profilers</a>: In-depth explanation of profiling in Python, including details on overhead and accuracy.</p>
</li>
<li><p><a target="_blank" href="https://wewake.dev/posts/practical-experiments-for-django-orm-query-optimizations/">Django Optimization Techniques</a>: Practical advice on optimizing Django ORM code in real-world applications.</p>
</li>
<li><p><a target="_blank" href="https://www.freecodecamp.org/news/python-magic-methods-practical-guide/">How Python Magic Methods Work: A Practical Guide</a>: My previous article on FreeCodeCamp covering practical deep-dive on Python Magic methods.</p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <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>
        
            <item>
                <title>
                    <![CDATA[ How to Index Your Next.js Apps Faster Using IndexNow ]]>
                </title>
                <description>
                    <![CDATA[ Next.js is a powerful framework for building lightning-fast applications. However, getting these applications indexed quickly by search engines is crucial for visibility and traffic, and sadly, this is not immediate. Even after uploading your sitemap... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-index-nextjs-pages-with-indexnow/</link>
                <guid isPermaLink="false">66ba1c4206a331cb39bc1ab5</guid>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ SEO ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Vivek Sahu ]]>
                </dc:creator>
                <pubDate>Tue, 06 Aug 2024 13:38:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/08/IndexNowSites-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Next.js is a powerful framework for building lightning-fast applications. However, getting these applications indexed quickly by search engines is crucial for visibility and traffic, and sadly, this is not immediate.</p>
<p>Even after uploading your sitemap, it can take up to weeks or even months before search engines crawl your pages. In fact, if you have updated or added any new page, it can take weeks for the search engines to notice.</p>
<p>So, can we do any better?</p>
<p>In this article, you'll learn how to boost your Next.js app's SEO on major search engines like Bing, Yahoo, and so on with fast indexing using IndexNow.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-indexnow">What is IndexNow?</a><ul>
<li><a class="post-section-overview" href="#heading-how-does-it-work">How Does it Work?</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#steps">Steps</a><ul>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-high-level-steps">High Level Steps</a></li>
<li><a class="post-section-overview" href="#heading-how-to-prove-ownership-of-the-host">How to Prove Ownership of the Host</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-script-to-get-all-urls-from-your-sitemap">How to Create a Script to Get All URLs from Your Sitemap</a></li>
<li><a class="post-section-overview" href="#heading-how-to-call-the-indexnow-api">How to Call the IndexNow API</a></li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-next-steps-and-improvements">Next Steps and Improvements</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
<li><a class="post-section-overview" href="#heading-links-and-references">Links and References</a></li>
</ul>
<h2 id="heading-what-is-indexnow">What is IndexNow?</h2>
<p><a target="_blank" href="https://www.indexnow.org/">IndexNow</a> is a protocol that drastically reduces indexing time. Here's how it's defined on their website:</p>
<blockquote>
<p>IndexNow is an easy way for websites owners to instantly inform search engines about latest content changes on their website. In its simplest form, IndexNow is a simple ping so that search engines know that a URL and its content has been added, updated, or deleted, allowing search engines to quickly reflect this change in their search results.(<a target="_blank" href="https://www.indexnow.org/">Source: IndexNow Home</a>)</p>
</blockquote>
<p>It's a protocol that is adopted by the likes of Bing, Naver, Seznam.cz, Yandex, Yep and others. Google does not support this protocol as of the writing of this article.</p>
<p>This is natively integrated into many CMS like Wix, and there are many third party plugins for others like Drupal or WordPress. However, there is no native support in NextJS.</p>
<h3 id="heading-how-does-it-work">How Does it Work?</h3>
<p>Every time you update something, all you need to do is "ping" or call their API and inform them of the change. </p>
<p>When this information is received, the search engines can now prioritize crawling these URLs over the other URLs that are being crawled naturally.</p>
<p>In this guide, we'll walk through the process of integrating IndexNow into your existing Next.js app so that any changes to URLs can be submitted and indexed by the search engines.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<ul>
<li>A Next.js app.</li>
<li>A <strong>sitemap.xml</strong> for your Next.js app.</li>
</ul>
<h3 id="heading-high-level-steps">High Level Steps</h3>
<ol>
<li>We first need to "prove ownership" of the host for which URLs will be submitted.</li>
<li>Create a simple Node.js script to get all URLs from your sitemap.</li>
<li>Call the IndexNow API.</li>
</ol>
<h3 id="heading-how-to-prove-ownership-of-the-host">How to Prove Ownership of the Host</h3>
<p>Go to the <a target="_blank" href="https://www.bing.com/indexnow/getstarted"><code>IndexNow</code></a> <a target="_blank" href="https://www.bing.com/indexnow/getstarted">page</a> for Bing. Since there is no direct integration for Next.js, scroll down to the section of <a target="_blank" href="https://www.bing.com/indexnow/getstarted#implementation">manual integration</a>.</p>
<p>Click on "Generate" to generate a new API Key.</p>
<p>In your Next.js app, go to the <strong>public</strong> directory in your root. All the static content is rendered through this directory. Create a new file and store this API key:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Assuming API Key is "f34f184d10c049ef99aa7637cdc4ef04". Change according to yourr generated API Key</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"f34f184d10c049ef99aa7637cdc4ef04"</span> &gt; f34f184d10c049ef99aa7637cdc4ef04.txt
</code></pre>
<p>Build and run your Next.js app:</p>
<pre><code class="lang-bash">npm run build &amp;&amp; npm run start
</code></pre>
<p>Then confirm that the file is available in your path <code>/f34f184d10c049ef99aa7637cdc4ef04.txt</code>.</p>
<p>That is, opening <a target="_blank" href="https://localhost:3000/f34f184d10c049ef99aa7637cdc4ef04.txt">https://localhost:3000/f34f184d10c049ef99aa7637cdc4ef04.txt</a> should give a response with text "f34f184d10c049ef99aa7637cdc4ef04" on your browser.</p>
<p>Depending on your API Key, modify the above key value. Commit, push and deploy this changes to your production.</p>
<p>On successful deployment, verify that <code>&lt;Your URL&gt;/&lt;API Key&gt;.txt</code> renders <code>&lt;API Key&gt;</code> text. That is: <code>&lt;Your URL&gt;/f34f184d10c049ef99aa7637cdc4ef04.txt</code> should render <code>f34f184d10c049ef99aa7637cdc4ef04</code>.</p>
<h3 id="heading-how-to-create-a-script-to-get-all-urls-from-your-sitemap">How to Create a Script to Get All URLs from Your Sitemap</h3>
<p>First, create the Node script file:</p>
<pre><code class="lang-bash">touch lib/indexnow.js
</code></pre>
<p>Then add the code below:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> xml2js = <span class="hljs-built_in">require</span>(<span class="hljs-string">'xml2js'</span>);

<span class="hljs-comment">// Configuration</span>
<span class="hljs-keyword">const</span> sitemapUrl = <span class="hljs-string">'&lt;Your URL&gt;/sitemap.xml'</span>; <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Update</span>
<span class="hljs-keyword">const</span> host = <span class="hljs-string">'&lt;Your URL&gt;'</span>; <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Update</span>
<span class="hljs-keyword">const</span> key = <span class="hljs-string">'&lt;API Key&gt;'</span>; <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Update</span>
<span class="hljs-keyword">const</span> keyLocation = <span class="hljs-string">'https://&lt;Your URL&gt;/&lt;API Key&gt;.txt'</span>; <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Update</span>

<span class="hljs-keyword">const</span> modifiedSinceDate = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(process.argv[<span class="hljs-number">2</span>] || <span class="hljs-string">'1970-01-01'</span>);

<span class="hljs-keyword">if</span> (<span class="hljs-built_in">isNaN</span>(modifiedSinceDate.getTime())) {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Invalid date provided. Please use format YYYY-MM-DD'</span>);
  process.exit(<span class="hljs-number">1</span>);
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchSitemap</span>(<span class="hljs-params">url</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    https.get(url, <span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
      <span class="hljs-keyword">let</span> data = <span class="hljs-string">''</span>;
      res.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> {
        data += chunk;
      });
      res.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> {
        resolve(data);
      });
    }).on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
      reject(err);
    });
  });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">parseSitemap</span>(<span class="hljs-params">xmlData</span>) </span>{
  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    xml2js.parseString(xmlData, <span class="hljs-function">(<span class="hljs-params">err, result</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (err) {
        reject(err);
      } <span class="hljs-keyword">else</span> {
        resolve(result);
      }
    });
  });
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">filterUrlsByDate</span>(<span class="hljs-params">sitemap, date</span>) </span>{
  <span class="hljs-keyword">const</span> urls = sitemap.urlset.url;
  <span class="hljs-keyword">return</span> urls
    .filter(<span class="hljs-function"><span class="hljs-params">url</span> =&gt;</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(url.lastmod[<span class="hljs-number">0</span>]) &gt; date)
    .map(<span class="hljs-function"><span class="hljs-params">url</span> =&gt;</span> url.loc[<span class="hljs-number">0</span>]);
}


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> xmlData = <span class="hljs-keyword">await</span> fetchSitemap(sitemapUrl);
    <span class="hljs-keyword">const</span> sitemap = <span class="hljs-keyword">await</span> parseSitemap(xmlData);
    <span class="hljs-keyword">const</span> filteredUrls = filterUrlsByDate(sitemap, modifiedSinceDate);
    <span class="hljs-built_in">console</span>.log(filteredUrls);
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'An error occurred:'</span>, error);
  }
}

main();
</code></pre>
<p>We basically fetch URLs from the sitemap that have been modified from a certain date. The date can be passed as an argument to the script.</p>
<p>Next, install the <code>xml2js</code> library which we'll use to parse the XML response from sitemap.</p>
<pre><code class="lang-bash">npm install xml2js --save-dev
</code></pre>
<p>Then run the script to fetch URLs and check if everything works:</p>
<pre><code class="lang-bash">node lib/indexnow.js 2024-01-01
</code></pre>
<p>This should output a list of URLs that have been modified since 1 Jan, 2024. You can pass any date to it.</p>
<h3 id="heading-how-to-call-the-indexnow-api">How to Call the IndexNow API</h3>
<p>Here's the IndexNow API Schema:</p>
<p><strong>Request:</strong></p>
<pre><code>POST /IndexNow HTTP/<span class="hljs-number">1.1</span>
Content-Type: application/json; charset=utf<span class="hljs-number">-8</span>
<span class="hljs-attr">Host</span>: api.indexnow.org
{
  <span class="hljs-string">"host"</span>: <span class="hljs-string">"www.example.org"</span>,
  <span class="hljs-string">"key"</span>: <span class="hljs-string">"7e3f6e8bc47b4f2380ba54aab6088521"</span>,
  <span class="hljs-string">"keyLocation"</span>: <span class="hljs-string">"https://www.example.org/7e3f6e8bc47b4f2380ba54aab6088521.txt"</span>,
  <span class="hljs-string">"urlList"</span>: [
      <span class="hljs-string">"https://www.example.org/url1"</span>,
      <span class="hljs-string">"https://www.example.org/folder/url2"</span>,
      <span class="hljs-string">"https://www.example.org/url3"</span>
      ]
}
</code></pre><p><strong>Response:</strong></p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>HTTP Code</td><td>Response</td><td>Reasons</td></tr>
</thead>
<tbody>
<tr>
<td>200</td><td>Ok</td><td>URL submitted successfully</td></tr>
<tr>
<td>400</td><td>Bad request</td><td>Invalid format</td></tr>
<tr>
<td>403</td><td>Forbidden</td><td>In case of key not valid (e.g., key not found, file found but key not in the file)</td></tr>
<tr>
<td>422</td><td>Unprocessable Entity</td><td>In case URLs don't belong to the host or the key is not matching the schema in the protocol</td></tr>
<tr>
<td>429</td><td>Too Many Requests</td><td>Too Many Requests (potential Spam)</td></tr>
</tbody>
</table>
</div><p>Now that we are certain that our URL fetching portion works correctly, let's add the main function to call the IndexNow API:</p>
<p>Open the <code>lib/index.js</code> file using your favorite IDE and add the following function:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">postToIndexNow</span>(<span class="hljs-params">urlList</span>) </span>{
  <span class="hljs-keyword">const</span> data = <span class="hljs-built_in">JSON</span>.stringify({
    host,
    key,
    keyLocation,
    urlList
  });

  <span class="hljs-keyword">const</span> options = {
    <span class="hljs-attr">hostname</span>: <span class="hljs-string">'api.indexnow.org'</span>,
    <span class="hljs-attr">port</span>: <span class="hljs-number">443</span>,
    <span class="hljs-attr">path</span>: <span class="hljs-string">'/IndexNow'</span>,
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json; charset=utf-8'</span>,
      <span class="hljs-string">'Content-Length'</span>: data.length
    }
  };

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> req = https.request(options, <span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
      <span class="hljs-keyword">let</span> responseData = <span class="hljs-string">''</span>;
      res.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> {
        responseData += chunk;
      });
      res.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> {
        resolve({
          <span class="hljs-attr">statusCode</span>: res.statusCode,
          <span class="hljs-attr">statusMessage</span>: res.statusMessage,
          <span class="hljs-attr">data</span>: responseData
        });
      });
    });

    req.on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
      reject(error);
    });

    req.write(data);
    req.end();
  });
}
</code></pre>
<p>This function makes the call to the IndexNow API by passing a list of URLs that is passed to it, along with the <code>&lt;API Key&gt;</code>.</p>
<p>Call this function from the main function. Modify the main function as following:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">main</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> xmlData = <span class="hljs-keyword">await</span> fetchSitemap(sitemapUrl);
    <span class="hljs-keyword">const</span> sitemap = <span class="hljs-keyword">await</span> parseSitemap(xmlData);
    <span class="hljs-keyword">const</span> filteredUrls = filterUrlsByDate(sitemap, modifiedSinceDate);
    <span class="hljs-built_in">console</span>.log(filteredUrls);

    <span class="hljs-keyword">if</span> (filteredUrls.length &gt; <span class="hljs-number">0</span>) {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> postToIndexNow(filteredUrls);
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'IndexNow API Response:'</span>);
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Status:'</span>, response.statusCode, response.statusMessage);
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data:'</span>, response.data);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'No URLs modified since the specified date.'</span>);
    }
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'An error occurred:'</span>, error);
  }
}
</code></pre>
<p>The <code>IndexNow API</code> will now be called for every URL that we pass to it.</p>
<p>Run the script, and you should see a similar output on success, or an error message in case of any issues:</p>
<pre><code>% node lib/indexnow.js <span class="hljs-number">2024</span><span class="hljs-number">-07</span><span class="hljs-number">-24</span>

[
  <span class="hljs-string">'https://&lt;Your URL&gt;'</span>,
  <span class="hljs-string">'https://&lt;Your URL&gt;/page1'</span>,
  <span class="hljs-string">'https://&lt;Your URL&gt;/page1'</span>
]
IndexNow API Response:
Status: <span class="hljs-number">200</span> OK
<span class="hljs-attr">Data</span>:
</code></pre><p>Voila, your APIs can now work with IndexNow for faster indexing.</p>
<h2 id="heading-next-steps-and-improvements">Next Steps and Improvements</h2>
<p>We just looked at how to write and execute a script locally to index our pages via IndexNow. However, there are many things that can be done to improve this further, for instance:</p>
<ul>
<li>Integrate IndexNow into your CI/CD pipeline for automatic updates.</li>
<li>Handle large sitemaps efficiently with batching or queuing.</li>
<li>Monitor and log IndexNow submissions for debugging and analytics.</li>
<li>Explore the IndexNow API for additional functionalities (for example: URL deletion).</li>
<li>Provide CLI version.</li>
<li>Add TypeScript support.</li>
</ul>
<p>However, these are out of scope for this article. There are few production ready npm modules that implement some or more of these advanced behaviors that can be easily integrated into your app. I have created one (<a target="_blank" href="https://wewake.dev/posts/introducing-indexnow-submitter-fast-search-engine-indexing/">see indexnow-submitter announcement</a>) as well as adding missing features/support in the ecosystem. You can plug and use any of these modules in your Node based applications.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you learned how to add the <code>IndexNow</code> protocol to your Next.js application. You can leverage this protocol to get a much faster, automated and convenient page indexing experience whenever you make any change to your website, allowing search engines to fetch the latest content from your pages. </p>
<p>I hope this was useful, and feel free to experiment and customize this integration further to suit your needs.</p>
<h2 id="heading-links-and-references">Links and References</h2>
<ul>
<li><a target="_blank" href="https://www.indexnow.org/documentation">IndexNow Documentation</a></li>
<li><a target="_blank" href="https://www.indexnow.org/faq">FAQ</a></li>
<li><a target="_blank" href="https://www.bing.com/indexnow/getstarted#implementation">IndexNow Bing</a></li>
<li><a target="_blank" href="https://www.npmjs.com/package/indexnow-submitter">indexnow-submitter NPM module</a></li>
<li><a target="_blank" href="https://wewake.dev/posts/introducing-indexnow-submitter-fast-search-engine-indexing/">indexnow-submitter release announcement</a> </li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
