<?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[ Bala Priya C - 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[ Bala Priya C - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 14 May 2026 04:32:16 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/balapriyac/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Efficient Data Processing in Python: Batch vs Streaming Pipelines Explained ]]>
                </title>
                <description>
                    <![CDATA[ Every data pipeline makes a fundamental choice before any code is written: does it process data in chunks on a schedule, or does it process data continuously as it arrives? This choice — batch versus  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/efficient-data-processing-in-python-batch-vs-streaming-pipelines/</link>
                <guid isPermaLink="false">69dcf4dbf57346bc1e06d19b</guid>
                
                    <category>
                        <![CDATA[ data-engineering ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Mon, 13 Apr 2026 13:51:23 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/0cd359d4-9628-4b17-8dc4-a3a2a83172c8.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Every data pipeline makes a fundamental choice before any code is written: does it process data in chunks on a schedule, or does it process data continuously as it arrives?</p>
<p>This choice — batch versus streaming — shapes the architecture of everything downstream. The tools you use, the guarantees you can make about data freshness, the complexity of your error handling, and the infrastructure you need to run it all follow directly from this decision.</p>
<p>Getting it wrong is expensive. Teams that build streaming pipelines when batch would have sufficed end up maintaining complex infrastructure for a problem that didn't require it.</p>
<p>Teams that build batch pipelines when their use case demands real-time processing discover the gap at the worst possible moment — when a stakeholder asks why the dashboard is six hours out of date.</p>
<p>In this article, you'll learn what batch and streaming pipelines actually are, how they differ in terms of architecture and tradeoffs, and how to implement both patterns in Python. By the end, you'll have a clear framework for choosing the right approach for any data engineering problem you solve.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along comfortably, make sure you have:</p>
<ul>
<li><p>Practice writing Python functions and working with modules</p>
</li>
<li><p>Familiarity with pandas DataFrames and basic data manipulation</p>
</li>
<li><p>A general understanding of what ETL pipelines do — extract, transform, load</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a href="#heading-what-is-a-batch-pipeline">What Is a Batch Pipeline?</a></p>
<ul>
<li><p><a href="#heading-implementing-a-batch-pipeline-in-python">Implementing a Batch Pipeline in Python</a></p>
</li>
<li><p><a href="#heading-when-batch-works-well">When Batch Works Well</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-what-is-a-streaming-pipeline">What Is a Streaming Pipeline?</a></p>
<ul>
<li><p><a href="#heading-implementing-a-streaming-pipeline-in-python">Implementing a Streaming Pipeline in Python</a></p>
</li>
<li><p><a href="#heading-when-streaming-works-well">When Streaming Works Well</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-the-key-differences-at-a-glance">The Key Differences at a Glance</a></p>
</li>
<li><p><a href="#heading-choosing-between-batch-and-streaming">Choosing Between Batch and Streaming</a></p>
</li>
<li><p><a href="#heading-the-hybrid-pattern-lambda-and-kappa-architectures">The Hybrid Pattern: Lambda and Kappa Architectures</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-batch-pipeline">What Is a Batch Pipeline?</h2>
<p>A batch pipeline processes a bounded, finite collection of records together — a file, a database snapshot, a day's worth of transactions. It runs on a schedule, say, hourly, nightly, weekly, reads all the data for that period, transforms it, and writes the result somewhere. Then it stops and waits until the next run.</p>
<p>The mental model is simple: <strong>collect, then process</strong>. Nothing happens between runs.</p>
<p>In a retail ETL context, a typical batch pipeline might look like this:</p>
<ol>
<li><p>At midnight, extract all orders placed in the last 24 hours from the transactional database</p>
</li>
<li><p>Join with the product catalogue and customer dimension tables</p>
</li>
<li><p>Compute daily revenue aggregates by region and product category</p>
</li>
<li><p>Load the results into the data warehouse for reporting</p>
</li>
</ol>
<p>The pipeline runs, finishes, and produces a complete, consistent snapshot of yesterday's business. By the time analysts arrive in the morning, the warehouse is up to date.</p>
<h3 id="heading-implementing-a-batch-pipeline-in-python">Implementing a Batch Pipeline in Python</h3>
<p>A batch pipeline in its simplest form is a Python script with three clearly separated stages: extract, transform, load.</p>
<pre><code class="language-python">import pandas as pd
from datetime import datetime, timedelta

def extract(filepath: str) -&gt; pd.DataFrame:
    """Load raw orders from a daily export file."""
    df = pd.read_csv(filepath, parse_dates=["order_timestamp"])
    return df

def transform(df: pd.DataFrame) -&gt; pd.DataFrame:
    """Clean and aggregate orders into daily revenue by region."""
    # Filter to completed orders only
    df = df[df["status"] == "completed"].copy()

    # Extract date from timestamp for grouping
    df["order_date"] = df["order_timestamp"].dt.date

    # Aggregate: total revenue and order count per region per day
    summary = (
        df.groupby(["order_date", "region"])
        .agg(
            total_revenue=("order_value_gbp", "sum"),
            order_count=("order_id", "count"),
            avg_order_value=("order_value_gbp", "mean"),
        )
        .reset_index()
    )
    return summary

def load(df: pd.DataFrame, output_path: str) -&gt; None:
    """Write the aggregated result to the warehouse (here, a CSV)."""
    df.to_csv(output_path, index=False)
    print(f"Loaded {len(df)} rows to {output_path}")

# Run the pipeline
raw = extract("orders_2024_06_01.csv")
aggregated = transform(raw)
load(aggregated, "warehouse/daily_revenue_2024_06_01.csv")
</code></pre>
<p>Let's walk through what this code is doing:</p>
<ul>
<li><p><code>extract</code> reads a CSV file representing a daily order export. The <code>parse_dates</code> argument tells pandas to interpret the <code>order_timestamp</code> column as a datetime object rather than a plain string — this matters for the date extraction step in transform.</p>
</li>
<li><p><code>transform</code> does two things: it filters out any orders that didn't complete (returns, cancellations), and then groups the remaining orders by date and region to produce revenue aggregates. The <code>.agg()</code> call computes three metrics per group in a single pass.</p>
</li>
<li><p><code>load</code> writes the result to a destination — in production this would be a database insert or a cloud storage upload, but the pattern is the same regardless.</p>
</li>
</ul>
<p>The three functions are deliberately kept separate. This separation — extract, transform, load — makes each stage independently testable, replaceable, and debuggable. If the transform logic changes, you don't need to modify the extract or load code.</p>
<h3 id="heading-when-batch-works-well">When Batch Works Well</h3>
<p>Batch pipelines are the right choice when:</p>
<ul>
<li><p><strong>Data freshness requirements are measured in hours, not seconds.</strong> A daily sales report doesn't need to be updated every minute. A weekly marketing attribution model certainly doesn't.</p>
</li>
<li><p><strong>You're processing large historical datasets.</strong> Backfilling two years of transaction history into a new data warehouse is inherently a batch job — the data exists, it's bounded, and you want to process it as efficiently as possible in one run.</p>
</li>
<li><p><strong>Consistency matters more than latency.</strong> Batch pipelines produce complete, point-in-time snapshots. Every row in the output was computed from the same input state. This consistency is valuable for financial reporting, regulatory compliance, and any downstream process that requires a stable, reproducible dataset.</p>
</li>
</ul>
<h2 id="heading-what-is-a-streaming-pipeline">What Is a Streaming Pipeline?</h2>
<p>A streaming pipeline processes data continuously, record by record or in small micro-batches, as it arrives. There is no "end" to the dataset — the pipeline runs indefinitely, consuming events from a source like a message queue, a Kafka topic, or a webhook, and processing each one as it comes in.</p>
<p>The mental model is: <strong>process as you collect</strong>. The pipeline is always running.</p>
<p>In the same retail ETL context, a streaming pipeline might handle order events as they're placed:</p>
<ol>
<li><p>An order is placed on the website and an event is published to a message queue</p>
</li>
<li><p>The streaming pipeline consumes the event within milliseconds</p>
</li>
<li><p>It validates, enriches, and routes the event to downstream systems</p>
</li>
<li><p>The fraud detection service, the inventory system, and the real-time dashboard all receive updated information immediately</p>
</li>
</ol>
<p>The difference from batch is fundamental: the data isn't sitting in a file waiting to be processed. It's flowing, and the pipeline has to keep up.</p>
<h3 id="heading-implementing-a-streaming-pipeline-in-python">Implementing a Streaming Pipeline in Python</h3>
<p>Python's generator functions are the natural building block for streaming pipelines. A generator produces values one at a time and pauses between yields — which maps directly onto the idea of processing records as they arrive without loading everything into memory.</p>
<pre><code class="language-python">import json
import time
from typing import Generator, Dict

def event_source(filepath: str) -&gt; Generator[Dict, None, None]:
    """
    Simulate a stream of order events from a file.
    In production, this would consume from Kafka or a message queue.
    """
    with open(filepath, "r") as f:
        for line in f:
            event = json.loads(line.strip())
            yield event
            time.sleep(0.01)  # simulate arrival delay between events

def validate(event: Dict) -&gt; bool:
    """Check that the event has the required fields and valid values."""
    required_fields = ["order_id", "customer_id", "order_value_gbp", "region"]
    if not all(field in event for field in required_fields):
        return False
    if event["order_value_gbp"] &lt;= 0:
        return False
    return True

def enrich(event: Dict) -&gt; Dict:
    """Add derived fields to the event before routing downstream."""
    event["processed_at"] = time.strftime("%Y-%m-%dT%H:%M:%S")
    event["value_tier"] = (
        "high"   if event["order_value_gbp"] &gt;= 500
        else "mid"    if event["order_value_gbp"] &gt;= 100
        else "low"
    )
    return event

def run_streaming_pipeline(source_file: str) -&gt; None:
    """Process each event as it arrives from the source."""
    processed = 0
    skipped = 0

    for raw_event in event_source(source_file):
        if not validate(raw_event):
            skipped += 1
            continue

        enriched_event = enrich(raw_event)

        # In production: publish to downstream topic or write to sink
        print(f"[{enriched_event['processed_at']}] "
              f"Order {enriched_event['order_id']} | "
              f"£{enriched_event['order_value_gbp']:.2f} | "
              f"tier={enriched_event['value_tier']}")
        processed += 1

    print(f"\nDone. Processed: {processed} | Skipped: {skipped}")

run_streaming_pipeline("order_events.jsonl")
</code></pre>
<p>Here's what's happening:</p>
<ul>
<li><p><code>event_source</code> is a generator function — note the <code>yield</code> keyword instead of <code>return</code>. Each call to <code>yield event</code> pauses the function and hands one event to the caller. The pipeline processes that event before the generator resumes and fetches the next one. This means only one event is in memory at a time, regardless of how large the stream is. The <code>time.sleep(0.01)</code> simulates the real-world delay between events arriving from a message queue.</p>
</li>
<li><p><code>validate</code> checks each event for required fields and valid values before doing anything else with it. In a streaming context, bad events are super common — network issues, upstream bugs, and schema changes all produce malformed records. Validating early and skipping invalid events is far safer than letting them propagate into downstream systems.</p>
</li>
<li><p><code>enrich</code> adds derived fields to the event. This can be a processing timestamp and a value tier classification. In production, this step might also join against a lookup table, call an external API, or apply a model prediction.</p>
</li>
<li><p><code>run_streaming_pipeline</code> ties it together. The <code>for</code> loop over <code>event_source</code> consumes events one at a time, processes each through the <code>validate → enrich → route</code> stages, and keeps a running count of processed and skipped events.</p>
</li>
</ul>
<h3 id="heading-when-streaming-works-well">When Streaming Works Well</h3>
<p>Streaming pipelines are the right choice when:</p>
<ul>
<li><p><strong>Data freshness is measured in seconds or milliseconds.</strong> Fraud detection, real-time inventory updates, live dashboards, and alerting systems all require data to be processed immediately — a batch job running every hour would make them useless.</p>
</li>
<li><p><strong>The data volume is too large to accumulate.</strong> High-frequency IoT sensor data, clickstream events, and financial tick data can generate millions of records per hour. Accumulating all of that before processing is often impractical – you'd need enormous storage and the processing job would take too long to be useful.</p>
</li>
<li><p><strong>You need to react, not just report.</strong> Streaming pipelines can trigger downstream actions — send a notification, block a transaction, update a recommendation — in response to individual events. Batch pipelines can only report on what already happened.</p>
</li>
</ul>
<h2 id="heading-the-key-differences-at-a-glance">The Key Differences at a Glance</h2>
<p>Here is an overview of the differences between batch and stream processing we've discussed thus far:</p>
<table>
<thead>
<tr>
<th><strong>DIMENSION</strong></th>
<th><strong>BATCH</strong></th>
<th><strong>STREAMING</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Data model</strong></td>
<td>Bounded, finite dataset</td>
<td>Unbounded, continuous flow</td>
</tr>
<tr>
<td><strong>Processing trigger</strong></td>
<td>Schedule (time or event)</td>
<td>Arrival of each record</td>
</tr>
<tr>
<td><strong>Latency</strong></td>
<td>Minutes to hours</td>
<td>Milliseconds to seconds</td>
</tr>
<tr>
<td><strong>Throughput</strong></td>
<td>High (optimized for bulk processing)</td>
<td>Lower per-record overhead</td>
</tr>
<tr>
<td><strong>Complexity</strong></td>
<td>Lower</td>
<td>Higher</td>
</tr>
<tr>
<td><strong>State management</strong></td>
<td>Stateless per run</td>
<td>Often stateful across events</td>
</tr>
<tr>
<td><strong>Error handling</strong></td>
<td>Retry the whole job</td>
<td>Per-event dead-letter queues</td>
</tr>
<tr>
<td><strong>Consistency</strong></td>
<td>Strong (point-in-time snapshot)</td>
<td>Eventually consistent</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>Reporting, ML training, backfills</td>
<td>Alerting, real-time features, event routing</td>
</tr>
</tbody></table>
<h2 id="heading-choosing-between-batch-and-streaming">Choosing Between Batch and Streaming</h2>
<p>Okay, all of this info is great. But <em>how</em> do you choose between batch and stream processing? The decision comes down to three questions:</p>
<p><strong>How fresh does the data need to be?</strong> If stakeholders can tolerate results that are hours old, batch is simpler and more cost-effective. If they need results within seconds, streaming is unavoidable.</p>
<p><strong>How complex is your processing logic?</strong> Batch jobs can join across large datasets, run expensive aggregations, and apply complex business logic without worrying about latency. Streaming pipelines must process each event quickly, which constrains how much work you can do per record.</p>
<p><strong>What's your operational capacity?</strong> Streaming infrastructure — Kafka clusters, Flink or Spark Streaming jobs, dead-letter queues, exactly-once delivery guarantees — is significantly more complex to operate than a scheduled Python script. If your team is small or your use case doesn't demand real-time results, that complexity is cost without benefit.</p>
<p>Start with batch. It's simpler to build, simpler to test, simpler to debug, and simpler to maintain. Move to streaming when a specific, concrete requirement — not a hypothetical future one — makes batch insufficient. Most data problems are batch problems, and the ones that genuinely require streaming are usually obvious when you run into them.</p>
<p>And as you might have guessed, you may need to combine them for some data processing systems. Which is why hybrid approaches exist.</p>
<h2 id="heading-the-hybrid-pattern-lambda-and-kappa-architectures">The Hybrid Pattern: Lambda and Kappa Architectures</h2>
<p>In practice, many production data systems use both patterns together. The two most common hybrid architectures are: Lambda and Kappa architecture.</p>
<p><a href="https://www.databricks.com/glossary/lambda-architecture"><strong>Lambda architecture</strong></a> runs a batch layer and a streaming layer in parallel. The batch layer processes complete historical data and produces accurate, consistent results on a delay. The streaming layer processes live data and produces approximate results immediately. Downstream consumers merge both outputs — using the streaming result for freshness and the batch result for correctness.</p>
<p>The tradeoff is operational complexity: you're maintaining two separate processing codebases that must produce semantically equivalent results.</p>
<p><a href="https://hazelcast.com/glossary/kappa-architecture/"><strong>Kappa architecture</strong></a> simplifies this by using only a streaming layer, but with the ability to replay historical data through the same pipeline when you need batch-style reprocessing. This works well when your streaming framework like <a href="https://kafka.apache.org/documentation/">Apache Kafka</a> and <a href="https://flink.apache.org/">Apache Flink</a> supports log retention and replay. You get one codebase, one set of logic, and the ability to reprocess history when your pipeline changes.</p>
<p>Neither architecture is universally better. Lambda is more common in organizations that adopted batch processing first and added streaming incrementally. Kappa is more common in systems designed with streaming as the primary pattern.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Batch and streaming are tools with different tradeoffs, each suited to a different class of problems. Batch pipelines excel at consistency, simplicity, and bulk throughput. Streaming pipelines excel at latency, reactivity, and continuous processing.</p>
<p>Understanding both patterns at the architectural level — before reaching for specific frameworks like Apache Spark, Kafka, or Flink — gives you the judgment to choose the right one and explain that choice clearly. The frameworks implement these patterns, while the judgment about which pattern fits your problem is yours to make first.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Command Pattern in Python ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever used an undo button in an app or scheduled tasks to run later? Both of these rely on the same idea: turning actions into objects. That's the command pattern. Instead of calling a method  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-command-pattern-in-python/</link>
                <guid isPermaLink="false">69c1abb330a9b81e3aa82e36</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Mon, 23 Mar 2026 21:08:03 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/85170982-e7e8-453a-9fd4-a7f2f4f7edb3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever used an undo button in an app or scheduled tasks to run later? Both of these rely on the same idea: <strong>turning actions into objects</strong>.</p>
<p>That's the command pattern. Instead of calling a method directly, you package the call – the action, its target, and any arguments – into an object. That object can be stored, passed around, executed later, or undone.</p>
<p>In this tutorial, you'll learn what the command pattern is and how to implement it in Python with a practical text editor example that supports undo.</p>
<p>You can find the code for this tutorial <a href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/command">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Basic understanding of Python classes and methods</p>
</li>
<li><p>Familiarity with object-oriented programming (OOP) concepts</p>
</li>
</ul>
<p>Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-the-command-pattern">What Is the Command Pattern?</a></p>
</li>
<li><p><a href="#heading-setting-up-the-receiver">Setting Up the Receiver</a></p>
</li>
<li><p><a href="#heading-defining-commands">Defining Commands</a></p>
</li>
<li><p><a href="#heading-the-invoker-running-and-undoing-commands">The Invoker: Running and Undoing Commands</a></p>
</li>
<li><p><a href="#heading-putting-it-all-together">Putting It All Together</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-command-pattern">When to Use the Command Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-command-pattern">What Is the Command Pattern?</h2>
<p>The <strong>command pattern</strong> is a behavioral design pattern that encapsulates a request as an object. This lets you:</p>
<ul>
<li><p><strong>Parameterize</strong> callers with different operations</p>
</li>
<li><p><strong>Queue or schedule</strong> operations for later execution</p>
</li>
<li><p><strong>Support undo/redo</strong> by keeping a history of executed commands</p>
</li>
</ul>
<p>The pattern has four key participants:</p>
<ul>
<li><p><strong>Command</strong>: an interface with an <code>execute()</code> method (and optionally <code>undo()</code>)</p>
</li>
<li><p><strong>Concrete Command</strong>: implements <code>execute()</code> and <code>undo()</code> for a specific action</p>
</li>
<li><p><strong>Receiver</strong>: the object that actually does the work (for example, a document)</p>
</li>
<li><p><strong>Invoker</strong>: triggers commands and manages history</p>
</li>
</ul>
<p>Think of a restaurant. The customer (client) tells the waiter (invoker) what they want. The waiter writes it on a ticket (command) and hands it to the kitchen (receiver). The waiter doesn't cook – they only manage tickets. If you change your mind, the waiter can cancel the ticket before it reaches the kitchen.</p>
<h2 id="heading-setting-up-the-receiver">Setting Up the Receiver</h2>
<p>We'll build a simple document editor. The <strong>receiver</strong> here is the <code>Document</code> class. It knows how to insert and delete text, but it has no idea who's calling it or why.</p>
<pre><code class="language-python">class Document:
    def __init__(self):
        self.content = ""

    def insert(self, text: str, position: int) -&gt; None:
        self.content = (
            self.content[:position] + text + self.content[position:]
        )

    def delete(self, position: int, length: int) -&gt; None:
        self.content = (
            self.content[:position] + self.content[position + length:]
        )

    def show(self) -&gt; None:
        print(f'Document: "{self.content}"')
</code></pre>
<p><code>insert</code> places text at a given position. <code>delete</code> removes <code>length</code> characters from a given position. Both are plain methods with no history or awareness of commands. And that's intentional.</p>
<h2 id="heading-defining-commands">Defining Commands</h2>
<p>Now let's define a base <code>Command</code> interface using an abstract class:</p>
<pre><code class="language-python">from abc import ABC, abstractmethod

class Command(ABC):
    @abstractmethod
    def execute(self) -&gt; None:
        pass

    @abstractmethod
    def undo(self) -&gt; None:
        pass
</code></pre>
<p>Any concrete command must implement both <code>execute</code> and <code>undo</code>. This is what makes a full history possible.</p>
<h3 id="heading-insertcommand"><code>InsertCommand</code></h3>
<p><code>InsertCommand</code> stores the text and position at creation time:</p>
<pre><code class="language-python">class InsertCommand(Command):
    def __init__(self, document: Document, text: str, position: int):
        self.document = document
        self.text = text
        self.position = position

    def execute(self) -&gt; None:
        self.document.insert(self.text, self.position)

    def undo(self) -&gt; None:
        self.document.delete(self.position, len(self.text))
</code></pre>
<p>When <code>execute()</code> is called, it inserts the text. When <code>undo()</code> is called, it deletes exactly what was inserted. Notice that <code>undo</code> is the inverse of <code>execute</code> – this is the key design requirement.</p>
<h3 id="heading-deletecommand"><code>DeleteCommand</code></h3>
<p>Now let's code the <code>DeleteCommand</code>:</p>
<pre><code class="language-python">class DeleteCommand(Command):
    def __init__(self, document: Document, position: int, length: int):
        self.document = document
        self.position = position
        self.length = length
        self._deleted_text = ""  # stored on execute, used on undo

    def execute(self) -&gt; None:
        self._deleted_text = self.document.content[
            self.position : self.position + self.length
        ]
        self.document.delete(self.position, self.length)

    def undo(self) -&gt; None:
        self.document.insert(self._deleted_text, self.position)
</code></pre>
<p><code>DeleteCommand</code> has one important detail: it captures the deleted text <em>during</em> <code>execute()</code>, not at creation time. This is because we don't know what text is at that position until the command actually runs. Without this, <code>undo()</code> wouldn't know what to restore.</p>
<h2 id="heading-the-invoker-running-and-undoing-commands">The Invoker: Running and Undoing Commands</h2>
<p>The <strong>invoker</strong> is the object that executes commands and keeps a history stack. It has no idea what a document is or how text editing works. It just manages command objects.</p>
<pre><code class="language-python">class EditorInvoker:
    def __init__(self):
        self._history: list[Command] = []

    def run(self, command: Command) -&gt; None:
        command.execute()
        self._history.append(command)

    def undo(self) -&gt; None:
        if not self._history:
            print("Nothing to undo.")
            return
        command = self._history.pop()
        command.undo()
        print("Undo successful.")
</code></pre>
<p><code>run()</code> executes the command and pushes it onto the history stack. <code>undo()</code> pops the last command and calls its <code>undo()</code> method. The stack naturally gives you the right order: last in, first undone.</p>
<h2 id="heading-putting-it-all-together">Putting It All Together</h2>
<p>Let's put it all together and walk through a real editing session:</p>
<pre><code class="language-python">doc = Document()
editor = EditorInvoker()

# Type a title
editor.run(InsertCommand(doc, "Quarterly Report", 0))
doc.show()

# Add a subtitle
editor.run(InsertCommand(doc, " - Finance", 16))
doc.show()

# Oops, wrong subtitle — undo it
editor.undo()
doc.show()

# Delete "Quarterly" and replace with "Annual"
editor.run(DeleteCommand(doc, 0, 9))
doc.show()

editor.run(InsertCommand(doc, "Annual", 0))
doc.show()

# Undo the insert
editor.undo()
doc.show()

# Undo the delete (restores "Quarterly")
editor.undo()
doc.show()
</code></pre>
<p>This outputs:</p>
<pre><code class="language-plaintext">Document: "Quarterly Report"
Document: "Quarterly Report - Finance"
Undo successful.
Document: "Quarterly Report"
Document: " Report"
Document: "Annual Report"
Undo successful.
Document: " Report"
Undo successful.
Document: "Quarterly Report"
</code></pre>
<p>Here's the step-by-step breakdown of how (and why) this works:</p>
<ul>
<li><p>Each <code>InsertCommand</code> and <code>DeleteCommand</code> carries its own instructions for both doing and undoing.</p>
</li>
<li><p><code>EditorInvoker</code> never looks inside a command. It only calls <code>execute()</code> and <code>undo()</code>.</p>
</li>
<li><p>The document (<code>Document</code>) never thinks about history. It mutates its content when told to.</p>
</li>
</ul>
<p>Each participant has a single, clear responsibility.</p>
<h2 id="heading-extending-with-macros">Extending with Macros</h2>
<p>One of the lesser-known benefits of the command pattern is that commands are just objects. So you can group them. Here's a <code>MacroCommand</code> that batches several commands and undoes them as a unit:</p>
<pre><code class="language-python">class MacroCommand(Command):
    def __init__(self, commands: list[Command]):
        self.commands = commands

    def execute(self) -&gt; None:
        for cmd in self.commands:
            cmd.execute()

    def undo(self) -&gt; None:
        for cmd in reversed(self.commands):
            cmd.undo()

# Apply a heading format in one shot: clear content, insert formatted title
macro = MacroCommand([
    DeleteCommand(doc, 0, len(doc.content)),
    InsertCommand(doc, "== Annual Report ==", 0),
])

editor.run(macro)
doc.show()

editor.undo()
doc.show()
</code></pre>
<p>This gives the following output:</p>
<pre><code class="language-plaintext">Document: "== Annual Report =="
Undo successful.
Document: "Quarterly Report"
</code></pre>
<p>The macro undoes its commands in reverse order. This is correct since the last thing done should be the first thing undone.</p>
<h2 id="heading-when-to-use-the-command-pattern">When to Use the Command Pattern</h2>
<p>The command pattern is a good fit when:</p>
<ul>
<li><p><strong>You need undo/redo</strong>: the pattern is practically made for this. Store executed commands in a stack and reverse them.</p>
</li>
<li><p><strong>You need to queue or schedule operations</strong>: commands are objects, so you can put them in a queue, serialize them, or delay execution.</p>
</li>
<li><p><strong>You want to decouple the caller from the action</strong>: the invoker doesn't need to know what the command does. It just runs it.</p>
</li>
<li><p><strong>You need to support macros or batched operations</strong>: group commands into a composite and run them together, as shown above.</p>
</li>
</ul>
<p>Avoid it when:</p>
<ul>
<li><p>The operations are simple and will never need undo or queuing. The pattern adds classes and indirection that may not be worth it for a simple CRUD action.</p>
</li>
<li><p>Commands would need to share so much state that the "encapsulate the request" idea breaks down.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you found this tutorial useful. To summarize, the command pattern turns actions into objects. And that single idea unlocks a lot: undo/redo, queuing, macros, and clean separation between who triggers an action and what the action does.</p>
<p>We built a document editor from scratch using <code>InsertCommand</code>, <code>DeleteCommand</code>, an <code>EditorInvoker</code> with a history stack, and a <code>MacroCommand</code> for batched edits. Each class knew exactly one thing and did it well.</p>
<p>As a next step, try extending the editor with a <code>RedoCommand</code>. You'll need a second stack alongside the history to bring back undone commands.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Recursion in Python – A Practical Introduction for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ Recursion is when a function solves a problem by calling itself. It sounds odd at first — why would a function call itself? — but once it clicks, you'll find it's often the most natural way to express ]]>
                </description>
                <link>https://www.freecodecamp.org/news/recursion-in-python-intro-for-beginners/</link>
                <guid isPermaLink="false">69b2e2721be92d8f1778c002</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Recursion ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Thu, 12 Mar 2026 15:57:38 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/4893014c-db1c-4a2b-9040-e3103faf5169.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recursion is when a function solves a problem by calling itself.</p>
<p>It sounds odd at first — why would a function call itself? — but once it clicks, you'll find it's often the most natural way to express certain kinds of problems in code.</p>
<p>In this article, you'll learn what recursion is, how it works under the hood, and how to use it in Python with examples that go from the basics all the way to practical real-world use cases.</p>
<p>You can get the code <a href="https://github.com/balapriyac/python-basics/tree/main/recursion">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we get started, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Basic understanding of Python functions and how they work</p>
</li>
<li><p>Familiarity with loops and conditionals</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-recursion">What Is Recursion?</a></p>
</li>
<li><p><a href="#heading-the-two-rules-of-every-recursive-function">The Two Rules of Every Recursive Function</a></p>
</li>
<li><p><a href="#heading-your-first-recursive-function">Your First Recursive Function</a></p>
</li>
<li><p><a href="#heading-how-python-handles-recursive-calls">How Python Handles Recursive Calls</a></p>
</li>
<li><p><a href="#heading-recursion-vs-iteration">Recursion vs Iteration</a></p>
</li>
<li><p><a href="#heading-working-with-nested-data">Working with Nested Data</a></p>
</li>
<li><p><a href="#heading-recursive-tree-traversal">Recursive Tree Traversal</a></p>
</li>
<li><p><a href="#heading-memoization-fixing-slow-recursion">Memoization: Fixing Slow Recursion</a></p>
</li>
<li><p><a href="#heading-pythons-recursion-limit">Python's Recursion Limit</a></p>
</li>
<li><p><a href="#heading-when-to-use-recursion">When to Use Recursion</a></p>
</li>
</ul>
<h2 id="heading-what-is-recursion">What Is Recursion?</h2>
<p>Recursion is a technique where a <strong>function solves a problem by breaking it into a smaller version of the same problem, and calling itself on that smaller version</strong>.</p>
<p>Think of a set of Russian nesting dolls or Matryoshka dolls. To find the smallest doll, you open the outer doll, then open the next one inside it, and keep going until there's nothing left to open. Each step is the same action — open the doll — just on a smaller doll than before.</p>
<p>That's recursion in a nutshell: the same action, applied to a shrinking problem, until you hit a point where there's nothing left to do.</p>
<h2 id="heading-the-two-rules-of-every-recursive-function">The Two Rules of Every Recursive Function</h2>
<p>Every correct recursive function must have exactly two things:</p>
<p><strong>1. A base case</strong> — the condition where the function stops calling itself and returns a result directly.</p>
<p><strong>2. A recursive case</strong> — the part where the function calls itself with a smaller or simpler version of the input.</p>
<p>If you forget the base case, the function will keep calling itself forever — until Python raises a <code>RecursionError</code>. We'll talk more about that later.</p>
<h2 id="heading-your-first-recursive-function">Your First Recursive Function</h2>
<p>Let's start with the classic example: calculating a factorial.</p>
<p>The factorial of <code>n</code> (written as <code>n!</code>) is the product of all integers from 1 to n. So <code>5! = 5 × 4 × 3 × 2 × 1 = 120</code>.</p>
<p>Notice the pattern: <code>5! = 5 × 4!</code>. And <code>4! = 4 × 3!</code>. Each factorial is just <code>n</code> multiplied by the factorial of the number below it. That's a perfect fit for recursion.</p>
<pre><code class="language-python">def factorial(n):
    # Base case: factorial of 0 or 1 is 1
    if n &lt;= 1:
        return 1
    # Recursive case: n! = n * (n-1)!
    return n * factorial(n - 1)

print(factorial(5))
print(factorial(10))
</code></pre>
<p>This outputs:</p>
<pre><code class="language-plaintext">120
3628800
</code></pre>
<p>The base case is <code>n &lt;= 1</code>. When we hit 0 or 1, we stop and return 1. The recursive case is <code>n * factorial(n - 1)</code>. We multiply <code>n</code> by the factorial of the number below it, trusting the function to figure out the rest.</p>
<h2 id="heading-how-python-handles-recursive-calls">How Python Handles Recursive Calls</h2>
<p>When a function calls itself, Python doesn't just replace the current call – it stacks them. Each call waits for the one below it to return a value before it can finish.</p>
<p>Let's trace <code>factorial(4)</code> step by step:</p>
<pre><code class="language-plaintext">factorial(4)
  └── 4 * factorial(3)
            └── 3 * factorial(2)
                      └── 2 * factorial(1)
                                └── returns 1   ← base case
                      └── 2 * 1 = 2
            └── 3 * 2 = 6
  └── 4 * 6 = 24
</code></pre>
<p>Each call is pushed onto Python's <strong>call stack</strong>. Once the base case returns, the stack unwinds — each waiting call gets its answer and finishes. This is why deep recursion can be a problem: too many stacked calls and Python runs out of stack space.</p>
<h2 id="heading-recursion-vs-iteration">Recursion vs Iteration</h2>
<p>Most problems you can solve recursively, you can also solve with a loop. Let's compare both approaches for summing a list of numbers.</p>
<p><strong>Iterative approach:</strong></p>
<pre><code class="language-python">def sum_iterative(numbers):
    total = 0
    for n in numbers:
        total += n
    return total
</code></pre>
<p><strong>Recursive approach:</strong></p>
<pre><code class="language-python">def sum_recursive(numbers):
    if not numbers:       # base case: empty list
        return 0
    return numbers[0] + sum_recursive(numbers[1:])

print(sum_recursive([10, 20, 30, 40]))
</code></pre>
<p>The recursive function call gives the following output:</p>
<pre><code class="language-plaintext">100
</code></pre>
<p>The recursive version says: <em>the sum of a list is the first element plus the sum of everything else</em>. The base case is an empty list, which sums to 0.</p>
<p>Both the iterative and recursive approaches work. Recursion tends to be more expressive. It's closer to how you'd describe the problem in plain English. Iteration tends to be more efficient in Python. Knowing when to use which one is a skill you'll develop with practice.</p>
<h2 id="heading-working-with-nested-data">Working with Nested Data</h2>
<p>Here's where recursion really starts to make sense. Loops are great for flat data, but nested structures — like a folder tree or a deeply nested dictionary — are often awkward to handle with loops alone.</p>
<p>Let's say you have a nested dictionary representing a product catalog, and you want to find all the prices buried inside it:</p>
<pre><code class="language-python">catalog = {
    "electronics": {
        "laptops": {
            "ThinkPad X1": 1299.99,
            "MacBook Air": 1099.99
        },
        "accessories": {
            "USB-C Hub": 49.99,
            "Laptop Stand": 34.99
        }
    },
    "stationery": {
        "Notebook A5": 8.99,
        "Gel Pen Set": 12.49
    }
}

def find_all_prices(data):
    prices = []
    for value in data.values():
        if isinstance(value, dict):
            # It's a nested dict — recurse into it
            prices.extend(find_all_prices(value))
        else:
            # It's a price — collect it
            prices.append(value)
    return prices

all_prices = find_all_prices(catalog)
print(f"All prices: {all_prices}")
print(f"Total inventory value: ${sum(all_prices):.2f}")
</code></pre>
<p>The function checks each value. If it's another dictionary, it recurses into it. If it's a number, it collects it.</p>
<p>Writing this with nested loops would require you to know the depth of the structure in advance. Recursion doesn't care how deep it goes.</p>
<p>Output:</p>
<pre><code class="language-plaintext">All prices: [1299.99, 1099.99, 49.99, 34.99, 8.99, 12.49]
Total inventory value: $2506.44
</code></pre>
<h2 id="heading-recursive-tree-traversal">Recursive Tree Traversal</h2>
<p>A <a href="https://en.wikipedia.org/wiki/Tree_(abstract_data_type)">tree</a> is a structure where each node can have child nodes, and each child node is itself a tree. That self-similar structure maps directly to a recursive function.</p>
<p>Let's build a simple file system tree and calculate the total size of all files:</p>
<pre><code class="language-python">class FileNode:
    def __init__(self, name, size=0, children=None):
        self.name = name
        self.size = size  # 0 for folders
        self.children = children or []

def total_size(node):
    # Base case: it's a file (no children)
    if not node.children:
        return node.size
    # Recursive case: sum this node's size + all children's sizes
    return node.size + sum(total_size(child) for child in node.children)

# Build a small file tree
project = FileNode("project", children=[
    FileNode("src", children=[
        FileNode("main.py", size=12400),
        FileNode("utils.py", size=5800),
    ]),
    FileNode("data", children=[
        FileNode("sales_jan.parquet", size=302914),
        FileNode("sales_feb.parquet", size=289000),
    ]),
    FileNode("README.md", size=3200)
])

print(f"Total project size: {total_size(project):,} bytes")
print(f"Source files only: {total_size(project.children[0]):,} bytes")
</code></pre>
<p>For each node, we either return its size directly (base case: it's a file) or add its size to the sum of all its children (recursive case: it's a folder). The structure of the code mirrors the structure of the tree.</p>
<p>Running the above should give the following output:</p>
<pre><code class="language-plaintext">Total project size: 613,314 bytes
Source files only: 18,200 bytes
</code></pre>
<h2 id="heading-memoization-fixing-slow-recursion">Memoization: Fixing Slow Recursion</h2>
<p>Recursion can sometimes do a lot of repeated work. The classic example is the <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence">Fibonacci sequence</a>, where each number is the sum of the two before it.</p>
<pre><code class="language-python">def fib(n):
    if n &lt;= 1:
        return n
    return fib(n - 1) + fib(n - 2)
</code></pre>
<p>This works, but <code>fib(35)</code> already takes a noticeable pause. The problem is that <code>fib(30)</code> gets calculated dozens of times across different branches.</p>
<p>The fix is <strong>memoization</strong> which involves caching results so each value is only computed once. Python makes this super simple with <code>functools.lru_cache</code>, which you can use like so:</p>
<pre><code class="language-python">from functools import lru_cache

@lru_cache(maxsize=None)
def fib_fast(n):
    if n &lt;= 1:
        return n
    return fib_fast(n - 1) + fib_fast(n - 2)

print(fib_fast(10))
print(fib_fast(50))
print(fib_fast(100))
</code></pre>
<p><strong>Output:</strong></p>
<pre><code class="language-plaintext">55
12586269025
354224848179261915075
</code></pre>
<p>Adding <code>@lru_cache</code> stores the result of each call. The next time <code>fib_fast(30)</code> is needed, it returns the cached value instantly instead of recalculating the entire subtree.</p>
<p><strong>Note</strong>: Memoization is worth reaching for any time your recursive function might solve the same subproblem more than once.</p>
<h2 id="heading-pythons-recursion-limit">Python's Recursion Limit</h2>
<p>Python sets a default recursion limit of <strong>1000 calls</strong>. If your function goes deeper than that, you'll get a <code>RecursionError</code>:</p>
<pre><code class="language-python">def countdown(n):
    if n == 0:
        return "Done"
    return countdown(n - 1)

print(countdown(5))     # works fine
print(countdown(2000))  # raises RecursionError
</code></pre>
<p>Here <code>countdown(5)</code> works and we get the <code>Done</code> message while <code>countdown(2000)</code> gives a <code>RecursionError</code> as it exceeds the preset recursion limit of 1000 recursive calls.</p>
<pre><code class="language-plaintext">Done
RecursionError: maximum recursion depth exceeded
</code></pre>
<p>You can raise the limit with <code>sys.setrecursionlimit()</code>, but this is usually a sign that iteration — or memoization — is the better tool for that particular problem.</p>
<pre><code class="language-python">import sys
sys.setrecursionlimit(5000)  # use with caution
</code></pre>
<p>For most tree traversals and <a href="https://en.wikipedia.org/wiki/Divide-and-conquer_algorithm">divide-and-conquer</a> algorithms, the default limit is more than enough. You'll only hit it when working with very deep input structures.</p>
<h2 id="heading-when-to-use-recursion">When to Use Recursion</h2>
<p>Recursion is a good fit when:</p>
<ul>
<li><p>The problem has a naturally self-similar structure — trees, graphs, nested data, file systems</p>
</li>
<li><p>You're implementing divide-and-conquer algorithms — merge sort, binary search, quicksort</p>
</li>
<li><p>The recursive solution is significantly clearer than the iterative equivalent</p>
</li>
<li><p>The depth of the structure is unknown at compile time</p>
</li>
</ul>
<p>Prefer iteration when:</p>
<ul>
<li><p>You're working with flat sequences — summing a list, searching an array</p>
</li>
<li><p>Performance is critical — Python doesn't optimise tail calls, so deep recursion has overhead</p>
</li>
<li><p>The input could be very large or deeply nested — risking a RecursionError</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Recursion takes a little time to feel natural, but the core idea is simple: solve a small version of the problem, trust the function to handle the rest, and always define a base case to stop.</p>
<p>You've covered the fundamentals: how the call stack works, how to handle nested data, tree traversal, and how to speed things up with memoization. These patterns come up repeatedly in practice, especially when working with file systems, parsers, and hierarchical data.</p>
<p>The best way to get comfortable with recursion is to pick a suitable problem and try writing it recursively before reaching for a loop. The thinking gets easier every time. You can also solve related programming challenges on HackerRank or Leetcode.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement the Strategy Pattern in Python ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever opened a food delivery app and chosen between "fastest route", "cheapest option", or "fewest stops"? Or picked a payment method at checkout like credit card, PayPal, or wallet balance? B ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-the-strategy-pattern-in-python/</link>
                <guid isPermaLink="false">69b1d33d6c896b0519c3abdc</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 11 Mar 2026 20:40:29 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/8298ed99-c958-4b98-821e-ae43496b85af.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever opened a food delivery app and chosen between "fastest route", "cheapest option", or "fewest stops"? Or picked a payment method at checkout like credit card, PayPal, or wallet balance? Behind both of these, there's a good chance the <strong>strategy pattern</strong> is at work.</p>
<p>The strategy pattern lets you define a family of algorithms, put each one in its own class, and make them interchangeable at runtime. Instead of writing a giant <code>if/elif</code> chain every time behavior needs to change, you swap in the right strategy for the job.</p>
<p>In this tutorial, you'll learn what the strategy pattern is, why it's useful, and how to implement it in Python with practical examples.</p>
<p>You can get the code <a href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/strategy">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Basic understanding of Python classes and methods</p>
</li>
<li><p>Familiarity with object-oriented programming (OOP) concepts</p>
</li>
</ul>
<p>Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a href="#heading-what-is-the-strategy-pattern">What Is the Strategy Pattern?</a></p>
</li>
<li><p><a href="#heading-a-simple-strategy-pattern-example">A Simple Strategy Pattern Example</a></p>
</li>
<li><p><a href="#heading-swapping-strategies-at-runtime">Swapping Strategies at Runtime</a></p>
</li>
<li><p><a href="#heading-using-abstract-base-classes">Using Abstract Base Classes</a></p>
</li>
<li><p><a href="#heading-when-to-use-the-strategy-pattern">When to Use the Strategy Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-strategy-pattern">What Is the Strategy Pattern?</h2>
<p>The <strong>strategy pattern</strong> defines a way to encapsulate a group of related algorithms so they can be used interchangeably. The object that uses the algorithm, called the <strong>context</strong>, doesn't need to know how it works. It just delegates the work to whichever strategy is currently set.</p>
<p>Think of it like a GPS app. The destination is the same, but you can switch between "avoid highways", "shortest distance", or "least traffic" without changing the destination or the app itself. Each routing option is a separate strategy.</p>
<p>The pattern is useful when:</p>
<ul>
<li><p>You have multiple variations of an algorithm or behavior</p>
</li>
<li><p>You want to eliminate long <code>if/elif</code> conditionals based on type</p>
</li>
<li><p>You want to swap behavior at runtime without changing the context class</p>
</li>
<li><p>Different parts of your app need different variations of the same operation</p>
</li>
</ul>
<p>Now let's look at examples to understand this better.</p>
<h2 id="heading-a-simple-strategy-pattern-example">A Simple Strategy Pattern Example</h2>
<p>Let's build a simple e-commerce order system where different discount strategies can be applied at checkout.</p>
<p>First, let's create the three discount strategies:</p>
<pre><code class="language-python">class RegularDiscount:
    def apply(self, price):
        return price * 0.95  # 5% off

class SeasonalDiscount:
    def apply(self, price):
        return price * 0.80  # 20% off

class NoDiscount:
    def apply(self, price):
        return price  # no change
</code></pre>
<p>Each class has a single <code>apply</code> method that takes a price and returns the discounted price. They <strong>share the same interface but implement different logic</strong>: that's the key concept in the strategy pattern.</p>
<p>Now let's create the <code>Order</code> class that uses one of these strategies:</p>
<pre><code class="language-python">class Order:
    def __init__(self, product, price, discount_strategy):
        self.product = product
        self.price = price
        self.discount_strategy = discount_strategy

    def final_price(self):
        return self.discount_strategy.apply(self.price)

    def summary(self):
        print(f"Product : {self.product}")
        print(f"Original: ${self.price:.2f}")
        print(f"Final   : ${self.final_price():.2f}")
        print("-" * 30)
</code></pre>
<p>The <code>Order</code> class is our <strong>context</strong>. It doesn't contain any discount logic itself – it delegates that entirely to <code>discount_strategy.apply()</code>. Whichever strategy object you pass in, that's the one that runs.</p>
<p>Now let's place some orders:</p>
<pre><code class="language-python">order1 = Order("Mechanical Keyboard", 120.00, NoDiscount())
order2 = Order("Laptop Stand", 45.00, RegularDiscount())
order3 = Order("USB-C Hub", 35.00, SeasonalDiscount())

order1.summary()
order2.summary()
order3.summary()
</code></pre>
<p>Running the above code should give you the following output:</p>
<pre><code class="language-plaintext">Product : Mechanical Keyboard
Original: $120.00
Final   : $120.00
------------------------------
Product : Laptop Stand
Original: $45.00
Final   : $42.75
------------------------------
Product : USB-C Hub
Original: $35.00
Final   : $28.00
------------------------------
</code></pre>
<p>Notice how <code>Order</code> never checks <code>if discount_type == "seasonal"</code>. It just calls <code>apply()</code> and trusts the strategy to handle it. Adding a new discount type in the future means creating one new class and nothing else changes.</p>
<h2 id="heading-swapping-strategies-at-runtime">Swapping Strategies at Runtime</h2>
<p>One of the biggest advantages of the strategy pattern is that you can change the strategy while the program is running. Let's say a user upgrades to a premium membership mid-session:</p>
<pre><code class="language-python">class ShoppingCart:
    def __init__(self):
        self.items = []
        self.discount_strategy = NoDiscount()  # default

    def add_item(self, name, price):
        self.items.append({"name": name, "price": price})

    def set_discount(self, strategy):
        self.discount_strategy = strategy
        print(f"Discount updated to: {strategy.__class__.__name__}")

    def checkout(self):
        print("\n--- Checkout Summary ---")
        total = 0
        for item in self.items:
            discounted = self.discount_strategy.apply(item["price"])
            print(f"{item['name']}: ${discounted:.2f}")
            total += discounted
        print(f"Total: ${total:.2f}\n")
</code></pre>
<p>The <code>set_discount</code> method lets us replace the strategy at any point. Let's see it in action:</p>
<pre><code class="language-python">cart = ShoppingCart()
cart.add_item("Notebook", 15.00)
cart.add_item("Desk Lamp", 40.00)
cart.add_item("Monitor Riser", 25.00)

# Checkout as a regular customer
cart.checkout()

# User upgrades to seasonal sale membership
cart.set_discount(SeasonalDiscount())
cart.checkout()
</code></pre>
<p>This outputs:</p>
<pre><code class="language-plaintext">--- Checkout Summary ---
Notebook: $15.00
Desk Lamp: $40.00
Monitor Riser: $25.00
Total: $80.00

Discount updated to: SeasonalDiscount

--- Checkout Summary ---
Notebook: $12.00
Desk Lamp: $32.00
Monitor Riser: $20.00
Total: $64.00
</code></pre>
<p>The cart itself didn't change – only the strategy did. This is the advantage of keeping <em>behavior</em> separate from the <em>context</em> that uses it.</p>
<h2 id="heading-using-abstract-base-classes">Using Abstract Base Classes</h2>
<p>So far, nothing enforces that every strategy has an <code>apply</code> method. If someone creates a strategy and forgets it, they'll get a cryptic <code>AttributeError</code> at runtime. We can prevent that using <a href="https://docs.python.org/3/library/abc.html">Python's Abstract Base Classes</a>.</p>
<pre><code class="language-python">from abc import ABC, abstractmethod

class DiscountStrategy(ABC):
    @abstractmethod
    def apply(self, price: float) -&gt; float:
        pass
</code></pre>
<p>Now let's rewrite our strategies to inherit from it:</p>
<pre><code class="language-python">class RegularDiscount(DiscountStrategy):
    def apply(self, price):
        return price * 0.95

class SeasonalDiscount(DiscountStrategy):
    def apply(self, price):
        return price * 0.80

class NoDiscount(DiscountStrategy):
    def apply(self, price):
        return price
</code></pre>
<p>Now if someone creates a broken strategy without <code>apply</code>, Python will raise a <code>TypeError</code> immediately when they try to instantiate it — before any code runs. That's a much cleaner failure.</p>
<pre><code class="language-python">class BrokenStrategy(DiscountStrategy):
    pass  # forgot to implement apply()

s = BrokenStrategy()  # raises TypeError right here
</code></pre>
<p>Using ABCs is especially helpful on larger teams or in shared codebases, where you want to make the contract explicit: every strategy <em>must</em> implement <code>apply</code>. Else, you run into an error as shown.</p>
<pre><code class="language-plaintext">      2     pass  # forgot to implement apply()
      3 
----&gt; 4 s = BrokenStrategy()  # raises TypeError right here

TypeError: Can't instantiate abstract class BrokenStrategy without an implementation for abstract method 'apply'
</code></pre>
<h2 id="heading-when-to-use-the-strategy-pattern">When to Use the Strategy Pattern</h2>
<p>The Strategy pattern is a good fit when:</p>
<ul>
<li><p>You have branching logic based on type — long <code>if/elif</code> blocks that check a "mode" or "type" variable are a signal that Strategy might help.</p>
</li>
<li><p>Behavior needs to change at runtime — when users or config values should be able to switch algorithms without restarting.</p>
</li>
<li><p>You're building extensible systems — new behavior can be added as a new class without touching existing code.</p>
</li>
<li><p>You want to test algorithms independently — each strategy is its own class, making unit tests straightforward.</p>
</li>
</ul>
<p>Avoid it when:</p>
<ul>
<li><p>You only have two variations that will never grow — a simple <code>if/else</code> is perfectly fine there.</p>
</li>
<li><p>The strategies share so much state that separating them into classes adds complexity without benefit.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you found this tutorial useful. To sum up, the strategy pattern gives you a clean way to manage varying behavior without polluting your classes with conditional logic. The context stays simple and stable and the strategies handle the complexity.</p>
<p>We covered the basic pattern, runtime strategy swapping, and enforcing contracts with abstract base classes. As with most design patterns, start simple: even without ABCs, separating your algorithms into their own classes immediately makes your code easier to read, test, and extend.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Implement the Observer Pattern in Python ]]>
                </title>
                <description>
                    <![CDATA[ Have you ever wondered how YouTube notifies you when your favorite channel uploads a new video? Or how your email client alerts you when new messages arrive? These are perfect examples of the observer pattern in action. The observer pattern is a desi... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-implement-the-observer-pattern-in-python/</link>
                <guid isPermaLink="false">6994c4a494993ba9dd1ad6ad</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Tue, 17 Feb 2026 19:42:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1771357332246/45dc3900-04d9-474e-91a8-bac2fec86c2c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Have you ever wondered how YouTube notifies you when your favorite channel uploads a new video? Or how your email client alerts you when new messages arrive? These are perfect examples of the observer pattern in action.</p>
<p>The observer pattern is a design pattern where an object (called the subject) maintains a list of dependents (called observers) and notifies them automatically when its state changes. It's like having a newsletter subscription: when new content is published, all subscribers get notified.</p>
<p>In this tutorial, you'll learn what the observer pattern is, why it's useful, and how to implement it in Python with practical examples.</p>
<p>You can find the code <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/observer">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Understanding of how Python classes and methods work</p>
</li>
<li><p>Familiarity with object-oriented programming (OOP) concepts</p>
</li>
</ul>
<p>Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-the-observer-pattern">What Is the Observer Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-simple-observer-pattern-example">A Simple Observer Pattern Example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-handling-unsubscribes">Handling Unsubscribes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-different-types-of-observers">Different Types of Observers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-abstract-base-classes">Using Abstract Base Classes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-the-observer-pattern">When to Use the Observer Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-observer-pattern">What Is the Observer Pattern?</h2>
<p>The observer pattern defines a <a target="_blank" href="https://en.wikipedia.org/wiki/One-to-many_\(data_model\)">one-to-many relationship</a> between objects. <strong>When one object changes state, all its dependents are notified and updated automatically</strong>.</p>
<p>Think of it like a news agency and reporters. When breaking news happens (the subject), the agency notifies all subscribed reporters (observers) immediately. Each reporter can then handle the news in their own way – some might tweet it, others might write articles, and some might broadcast it on TV.</p>
<p>The pattern is useful when:</p>
<ul>
<li><p>You need to notify multiple objects about state changes</p>
</li>
<li><p>You want loose coupling between objects</p>
</li>
<li><p>You don't know how many objects need to be notified in advance</p>
</li>
<li><p>Objects should be able to subscribe and unsubscribe dynamically</p>
</li>
</ul>
<h2 id="heading-a-simple-observer-pattern-example">A Simple Observer Pattern Example</h2>
<p>Let's start with a basic example: a blog that notifies readers when a new article is published.</p>
<p>We'll create a blog (subject) and email subscribers (observers) who get notified automatically when new content is posted.</p>
<p>First, let's build the <code>Blog</code> class that will manage subscribers and send notifications:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Blog</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name</span>):</span>
        self.name = name
        self._subscribers = []
        self._latest_post = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">subscribe</span>(<span class="hljs-params">self, subscriber</span>):</span>
        <span class="hljs-string">"""Add a subscriber to the blog"""</span>
        <span class="hljs-keyword">if</span> subscriber <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._subscribers:
            self._subscribers.append(subscriber)
            print(<span class="hljs-string">f"✓ <span class="hljs-subst">{subscriber.email}</span> subscribed to <span class="hljs-subst">{self.name}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">unsubscribe</span>(<span class="hljs-params">self, subscriber</span>):</span>
        <span class="hljs-string">"""Remove a subscriber from the blog"""</span>
        <span class="hljs-keyword">if</span> subscriber <span class="hljs-keyword">in</span> self._subscribers:
            self._subscribers.remove(subscriber)
            print(<span class="hljs-string">f"✗ <span class="hljs-subst">{subscriber.email}</span> unsubscribed from <span class="hljs-subst">{self.name}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">notify_all</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Send notifications to all subscribers"""</span>
        print(<span class="hljs-string">f"\nNotifying <span class="hljs-subst">{len(self._subscribers)}</span> subscribers..."</span>)
        <span class="hljs-keyword">for</span> subscriber <span class="hljs-keyword">in</span> self._subscribers:
            subscriber.receive_notification(self.name, self._latest_post)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">publish_post</span>(<span class="hljs-params">self, title</span>):</span>
        <span class="hljs-string">"""Publish a new post and notify subscribers"""</span>
        print(<span class="hljs-string">f"\n📝 <span class="hljs-subst">{self.name}</span> published: '<span class="hljs-subst">{title}</span>'"</span>)
        self._latest_post = title
        self.notify_all()
</code></pre>
<p>The <code>Blog</code> class is our subject. It maintains a list of subscribers in <code>_subscribers</code> and stores the latest post title in <code>_latest_post</code>. The <code>subscribe</code> method adds subscribers to the list, checking for duplicates. The <code>notify_all</code> method loops through all subscribers and calls their <code>receive_notification</code> method. When we call <code>publish_post</code>, it updates the latest post and automatically notifies all subscribers.</p>
<p>Now let's create the observer class that receives notifications:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailSubscriber</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, email</span>):</span>
        self.email = email

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">receive_notification</span>(<span class="hljs-params">self, blog_name, post_title</span>):</span>
        print(<span class="hljs-string">f"📧 Email sent to <span class="hljs-subst">{self.email}</span>: New post on <span class="hljs-subst">{blog_name}</span> - '<span class="hljs-subst">{post_title}</span>'"</span>)
</code></pre>
<p>The <code>EmailSubscriber</code> class is our observer. It has one method, <code>receive_notification</code>, which handles incoming notifications from the blog.</p>
<p>Now let's use these classes together:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a blog</span>
tech_blog = Blog(<span class="hljs-string">"DevDaily"</span>)

<span class="hljs-comment"># Create subscribers</span>
reader1 = EmailSubscriber(<span class="hljs-string">"anna@example.com"</span>)
reader2 = EmailSubscriber(<span class="hljs-string">"betty@example.com"</span>)
reader3 = EmailSubscriber(<span class="hljs-string">"cathy@example.com"</span>)

<span class="hljs-comment"># Subscribe to the blog</span>
tech_blog.subscribe(reader1)
tech_blog.subscribe(reader2)
tech_blog.subscribe(reader3)

<span class="hljs-comment"># Publish posts</span>
tech_blog.publish_post(<span class="hljs-string">"10 Python Tips for Beginners"</span>)
tech_blog.publish_post(<span class="hljs-string">"Understanding Design Patterns"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">✓ anna@example.com subscribed to DevDaily
✓ betty@example.com subscribed to DevDaily
✓ cathy@example.com subscribed to DevDaily

📝 DevDaily published: '10 Python Tips for Beginners'

Notifying 3 subscribers...
📧 Email sent to anna@example.com: New post on DevDaily - '10 Python Tips for Beginners'
📧 Email sent to betty@example.com: New post on DevDaily - '10 Python Tips for Beginners'
📧 Email sent to cathy@example.com: New post on DevDaily - '10 Python Tips for Beginners'

📝 DevDaily published: 'Understanding Design Patterns'

Notifying 3 subscribers...
📧 Email sent to anna@example.com: New post on DevDaily - 'Understanding Design Patterns'
📧 Email sent to betty@example.com: New post on DevDaily - 'Understanding Design Patterns'
📧 Email sent to cathy@example.com: New post on DevDaily - 'Understanding Design Patterns'
</code></pre>
<p>Notice how the <code>Blog</code> class doesn't need to know the details of how each subscriber handles the notification. It just calls their <code>receive_notification</code> method.</p>
<p><strong>Note</strong>: Think of all the examples here as placeholder functions that explain how the observer pattern works. In your projects, you’ll have functions that connect to email and other services.</p>
<h2 id="heading-handling-unsubscribes">Handling Unsubscribes</h2>
<p>In real applications, users need to be able to unsubscribe. Here's how that works:</p>
<pre><code class="lang-python">blog = Blog(<span class="hljs-string">"CodeMaster"</span>)

user1 = EmailSubscriber(<span class="hljs-string">"john@example.com"</span>)
user2 = EmailSubscriber(<span class="hljs-string">"jane@example.com"</span>)

<span class="hljs-comment"># Subscribe users</span>
blog.subscribe(user1)
blog.subscribe(user2)

<span class="hljs-comment"># Publish a post</span>
blog.publish_post(<span class="hljs-string">"Getting Started with Python"</span>)

<span class="hljs-comment"># User1 unsubscribes</span>
blog.unsubscribe(user1)

<span class="hljs-comment"># Publish another post - only user2 gets notified</span>
blog.publish_post(<span class="hljs-string">"Advanced Python Techniques"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">✓ john@example.com subscribed to CodeMaster
✓ jane@example.com subscribed to CodeMaster

📝 CodeMaster published: 'Getting Started with Python'

Notifying 2 subscribers...
📧 Email sent to john@example.com: New post on CodeMaster - 'Getting Started with Python'
📧 Email sent to jane@example.com: New post on CodeMaster - 'Getting Started with Python'
✗ john@example.com unsubscribed from CodeMaster

📝 CodeMaster published: 'Advanced Python Techniques'

Notifying 1 subscribers...
📧 Email sent to jane@example.com: New post on CodeMaster - 'Advanced Python Techniques'
</code></pre>
<p>After <code>user1</code> unsubscribes, only <code>user2</code> receives the notification for the second post. The observer pattern makes it easy to add and remove observers dynamically.</p>
<h2 id="heading-different-types-of-observers">Different Types of Observers</h2>
<p>One super useful aspect of the observer pattern is that different observers can react differently to the same event. Let's create a stock price tracker where multiple observer types respond to price changes.</p>
<p>First, let's create the <code>Stock</code> class that will notify observers when the price changes:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Stock</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        self.symbol = symbol
        self._price = price
        self._observers = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_observer</span>(<span class="hljs-params">self, observer</span>):</span>
        self._observers.append(observer)
        print(<span class="hljs-string">f"Observer added: <span class="hljs-subst">{observer.__class__.__name__}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">remove_observer</span>(<span class="hljs-params">self, observer</span>):</span>
        self._observers.remove(observer)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">notify_observers</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">for</span> observer <span class="hljs-keyword">in</span> self._observers:
            observer.update(self.symbol, self._price)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">set_price</span>(<span class="hljs-params">self, price</span>):</span>
        print(<span class="hljs-string">f"\n <span class="hljs-subst">{self.symbol}</span> price changed: $<span class="hljs-subst">{self._price}</span> → $<span class="hljs-subst">{price}</span>"</span>)
        self._price = price
        self.notify_observers()
</code></pre>
<p>The <code>Stock</code> class maintains the current price and notifies all observers whenever <code>set_price</code> is called.</p>
<p>Now let's create three different observer types that respond differently to price updates:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailAlert</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, email</span>):</span>
        self.email = email

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        print(<span class="hljs-string">f"📧 Sending email to <span class="hljs-subst">{self.email}</span>: <span class="hljs-subst">{symbol}</span> is now $<span class="hljs-subst">{price}</span>"</span>)

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

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        print(<span class="hljs-string">f"📱 Sending SMS to <span class="hljs-subst">{self.phone}</span>: <span class="hljs-subst">{symbol}</span> price update $<span class="hljs-subst">{price}</span>"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Logger</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, symbol, price</span>):</span>
        print(<span class="hljs-string">f"📝 Logging: <span class="hljs-subst">{symbol}</span> = $<span class="hljs-subst">{price}</span> at system time"</span>)
</code></pre>
<p>Each observer has a different implementation of the update method. <code>EmailAlert</code> sends emails, <code>SMSAlert</code> sends text messages, and <code>Logger</code> records the change.</p>
<p>Now let's use them together:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a stock</span>
apple_stock = Stock(<span class="hljs-string">"AAPL"</span>, <span class="hljs-number">150.00</span>)

<span class="hljs-comment"># Create different types of observers</span>
email_notifier = EmailAlert(<span class="hljs-string">"investor@example.com"</span>)
sms_notifier = SMSAlert(<span class="hljs-string">"+1234567890"</span>)
price_logger = Logger()

<span class="hljs-comment"># Add all observers</span>
apple_stock.add_observer(email_notifier)
apple_stock.add_observer(sms_notifier)
apple_stock.add_observer(price_logger)

<span class="hljs-comment"># Update the stock price</span>
apple_stock.set_price(<span class="hljs-number">155.50</span>)
apple_stock.set_price(<span class="hljs-number">152.25</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Observer added: EmailAlert
Observer added: SMSAlert
Observer added: Logger

 AAPL price changed: $150.0 → $155.5
📧 Sending email to investor@example.com: AAPL is now $155.5
📱 Sending SMS to +1234567890: AAPL price update $155.5
📝 Logging: AAPL = $155.5 at system time

 AAPL price changed: $155.5 → $152.25
📧 Sending email to investor@example.com: AAPL is now $152.25
📱 Sending SMS to +1234567890: AAPL price update $152.25
📝 Logging: AAPL = $152.25 at system time
</code></pre>
<p>The <code>Stock</code> class doesn't care what each observer does. It simply calls <code>update</code> on each one and passes the necessary data. You can mix and match observers however you want.</p>
<h2 id="heading-using-abstract-base-classes">Using Abstract Base Classes</h2>
<p>To enforce a consistent interface across all observers, we can use Python's <a target="_blank" href="https://docs.python.org/3/library/abc.html">Abstract Base Classes</a>. This guarantees type safety.</p>
<p>First, let's create the base classes that define our interface:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Subject</span>(<span class="hljs-params">ABC</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._observers = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">attach</span>(<span class="hljs-params">self, observer</span>):</span>
        <span class="hljs-keyword">if</span> observer <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self._observers:
            self._observers.append(observer)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">detach</span>(<span class="hljs-params">self, observer</span>):</span>
        self._observers.remove(observer)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">notify</span>(<span class="hljs-params">self, data</span>):</span>
        <span class="hljs-keyword">for</span> observer <span class="hljs-keyword">in</span> self._observers:
            observer.update(data)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Observer</span>(<span class="hljs-params">ABC</span>):</span>
<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>The <code>Subject</code> class provides standard observer management methods. The <code>Observer</code> class defines the interface with the <code>@abstractmethod</code> decorator ensuring all observers implement update.</p>
<p>Now let's create an order system that uses these base classes:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderSystem</span>(<span class="hljs-params">Subject</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>
        super().__init__()
        self._order_id = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">place_order</span>(<span class="hljs-params">self, order_id, items</span>):</span>
        print(<span class="hljs-string">f"\n🛒 Order #<span class="hljs-subst">{order_id}</span> placed with <span class="hljs-subst">{len(items)}</span> items"</span>)
        self._order_id = order_id
        self.notify({<span class="hljs-string">"order_id"</span>: order_id, <span class="hljs-string">"items"</span>: items})
</code></pre>
<p>The <code>OrderSystem</code> inherits from <code>Subject</code> and can manage observers without implementing that logic itself.</p>
<p>Next, let's create concrete observers for different departments:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InventoryObserver</span>(<span class="hljs-params">Observer</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        print(<span class="hljs-string">f"📦 Inventory: Updating stock for order #<span class="hljs-subst">{data[<span class="hljs-string">'order_id'</span>]}</span>"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ShippingObserver</span>(<span class="hljs-params">Observer</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        print(<span class="hljs-string">f"🚚 Shipping: Preparing shipment for order #<span class="hljs-subst">{data[<span class="hljs-string">'order_id'</span>]}</span>"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BillingObserver</span>(<span class="hljs-params">Observer</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, data</span>):</span>
        print(<span class="hljs-string">f"💳 Billing: Processing payment for order #<span class="hljs-subst">{data[<span class="hljs-string">'order_id'</span>]}</span>"</span>)
</code></pre>
<p>Each observer <em>must</em> implement the <code>update</code> method. Now let's put it all together:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create the order system</span>
order_system = OrderSystem()

<span class="hljs-comment"># Create observers</span>
inventory = InventoryObserver()
shipping = ShippingObserver()
billing = BillingObserver()

<span class="hljs-comment"># Attach observers</span>
order_system.attach(inventory)
order_system.attach(shipping)
order_system.attach(billing)

<span class="hljs-comment"># Place an order</span>
order_system.place_order(<span class="hljs-string">"ORD-12345"</span>, [<span class="hljs-string">"Laptop"</span>, <span class="hljs-string">"Mouse"</span>, <span class="hljs-string">"Keyboard"</span>])
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">🛒 Order #ORD-12345 placed with 3 items
📦 Inventory: Updating stock for order #ORD-12345
🚚 Shipping: Preparing shipment for order #ORD-12345
💳 Billing: Processing payment for order #ORD-12345
</code></pre>
<p>Using abstract base classes provides type safety and ensures all observers follow the same interface.</p>
<h2 id="heading-when-to-use-the-observer-pattern">When to Use the Observer Pattern</h2>
<p>The observer pattern is suiatble for:</p>
<ul>
<li><p>Event-driven systems – GUI frameworks, game engines, or any system where actions trigger updates elsewhere.</p>
</li>
<li><p>Real-time notifications – Chat apps, social media feeds, stock tickers, or push notification systems.</p>
</li>
<li><p>Decoupled architecture – When you want the subject independent of its observers for flexibility.</p>
</li>
<li><p>Multiple listeners – When multiple objects need to react to the same event differently.</p>
</li>
</ul>
<p>Avoid the Observer Pattern when you have simple one-to-one relationships, or when performance is critical with many observers (because notification overhead can be significant).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The observer pattern creates a clean separation between objects that produce events and objects that respond to them. It promotes loose coupling – the subject doesn't need to know anything about its observers except that they have an update method.</p>
<p>We've covered the basic implementation, handling subscriptions, using different observer types, and abstract base classes. Start simple with the basic subject-observer relationship and add complexity only when needed.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Factory Pattern in Python - A Practical Guide ]]>
                </title>
                <description>
                    <![CDATA[ Design patterns are proven solutions to common problems in software development. If you've ever found yourself writing repetitive object creation code or struggling to manage different types of objects, the factory pattern might be exactly what you n... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-factory-pattern-in-python-a-practical-guide/</link>
                <guid isPermaLink="false">6989f75b7982b0d48a3c2c48</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ design patterns ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Mon, 09 Feb 2026 15:03:55 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770649418899/f26d3d70-a909-4d8f-92f5-7f263c64f9fe.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Design patterns are proven solutions to common problems in software development. If you've ever found yourself writing repetitive object creation code or struggling to manage different types of objects, the <strong>factory pattern</strong> might be exactly what you need.</p>
<p>In this tutorial, you'll learn what the factory pattern is, why it's useful, and how to implement it in Python. We'll build practical examples that show you when and how to use this pattern in real-world applications.</p>
<p>You can find the code <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/factory">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Understanding of Python classes and methods</p>
</li>
<li><p>Familiarity with <a target="_blank" href="https://www.youtube.com/watch?v=Ej_02ICOIgs">object-oriented programming</a> (OOP) concepts</p>
</li>
</ul>
<p>Let’s get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-the-factory-pattern">What Is the Factory Pattern?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-simple-factory-example">A Simple Factory Example</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-a-dictionary-for-cleaner-code">Using a Dictionary for Cleaner Code</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-factory-pattern-with-parameters">Factory Pattern with Parameters</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-abstract-base-classes">Using Abstract Base Classes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-more-helpful-example-database-connection-factory">A More Helpful Example: Database Connection Factory</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-the-factory-pattern">When to Use the Factory Pattern</a></p>
</li>
</ul>
<h2 id="heading-what-is-the-factory-pattern">What Is the Factory Pattern?</h2>
<p>The factory pattern is a creational design pattern that <strong>provides an interface for creating objects without specifying their exact classes</strong>. Instead of calling a constructor directly, you call a factory method that decides which class to instantiate.</p>
<p>Think of it like ordering food at a restaurant. You don't go into the kitchen and make the food yourself. You tell the waiter what you want, and the kitchen (the factory) creates it for you. You get your meal without worrying about the recipe or cooking process.</p>
<p>The factory pattern is useful when:</p>
<ul>
<li><p>You have multiple related classes and need to decide which one to instantiate at runtime</p>
</li>
<li><p>Object creation logic is complex and you want to encapsulate it</p>
</li>
<li><p>You want to make your code more maintainable and testable</p>
</li>
</ul>
<h2 id="heading-a-simple-factory-example">A Simple Factory Example</h2>
<p>Let's start with a basic example. Say you're building a notification system that can send messages via email, SMS, or push notifications.</p>
<p>Without a factory, you might write code like this everywhere in your application:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Bad approach - tight coupling</span>
<span class="hljs-keyword">if</span> notification_type == <span class="hljs-string">"email"</span>:
    notifier = EmailNotifier()
<span class="hljs-keyword">elif</span> notification_type == <span class="hljs-string">"sms"</span>:
    notifier = SMSNotifier()
<span class="hljs-keyword">elif</span> notification_type == <span class="hljs-string">"push"</span>:
    notifier = PushNotifier()
</code></pre>
<p>This gets messy quickly. Let's use a factory instead:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailNotifier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self, message</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Sending email: <span class="hljs-subst">{message}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SMSNotifier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self, message</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Sending SMS: <span class="hljs-subst">{message}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PushNotifier</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self, message</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Sending push notification: <span class="hljs-subst">{message}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotificationFactory</span>:</span>
<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_notifier</span>(<span class="hljs-params">notifier_type</span>):</span>
        <span class="hljs-keyword">if</span> notifier_type == <span class="hljs-string">"email"</span>:
            <span class="hljs-keyword">return</span> EmailNotifier()
        <span class="hljs-keyword">elif</span> notifier_type == <span class="hljs-string">"sms"</span>:
            <span class="hljs-keyword">return</span> SMSNotifier()
        <span class="hljs-keyword">elif</span> notifier_type == <span class="hljs-string">"push"</span>:
            <span class="hljs-keyword">return</span> PushNotifier()
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown notifier type: <span class="hljs-subst">{notifier_type}</span>"</span>)
</code></pre>
<p>In this code, we define three notifier classes, each with a send method.</p>
<p><strong>Note</strong>: In a real application, these would have different implementations for sending notifications.</p>
<p>The <code>NotificationFactory</code> class has a <a target="_blank" href="https://docs.python.org/3/library/functions.html#staticmethod">static method</a> called <code>create_notifier</code>. This is our factory method. It takes a string parameter and returns the appropriate notifier object.</p>
<p>The <code>@staticmethod</code> decorator means we can call this method without creating an instance of the factory. We just use <code>NotificationFactory.create_notifier()</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Using the factory</span>
notifier = NotificationFactory.create_notifier(<span class="hljs-string">"email"</span>)
result = notifier.send(<span class="hljs-string">"Hello, World!"</span>)
</code></pre>
<p>Now, whenever we need a notifier, we call the factory instead of instantiating classes directly. This centralizes our object creation logic in one place.</p>
<h2 id="heading-using-a-dictionary-for-cleaner-code">Using a Dictionary for Cleaner Code</h2>
<p>The if-elif chain in our factory can get unwieldy as we add more notifier types. Let's refactor using a dictionary:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">NotificationFactory</span>:</span>
    notifier_types = {
        <span class="hljs-string">"email"</span>: EmailNotifier,
        <span class="hljs-string">"sms"</span>: SMSNotifier,
        <span class="hljs-string">"push"</span>: PushNotifier
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_notifier</span>(<span class="hljs-params">notifier_type</span>):</span>
        notifier_class = NotificationFactory.notifier_types.get(notifier_type)
        <span class="hljs-keyword">if</span> notifier_class:
            <span class="hljs-keyword">return</span> notifier_class()
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown notifier type: <span class="hljs-subst">{notifier_type}</span>"</span>)
</code></pre>
<p>This approach is much cleaner. We store a dictionary that maps strings to class objects and <em>not</em> instances. The keys are notifier type names, and the values are the actual class references.</p>
<p>The <code>get</code> method retrieves the class from the dictionary. If the key doesn't exist, it returns <code>None</code>. We then instantiate the class by calling it with parentheses: <code>notifier_class()</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Test with different types</span>
email_notifier = NotificationFactory.create_notifier(<span class="hljs-string">"email"</span>)
sms_notifier = NotificationFactory.create_notifier(<span class="hljs-string">"sms"</span>)
push_notifier = NotificationFactory.create_notifier(<span class="hljs-string">"push"</span>)
</code></pre>
<p>This makes adding new notifier types easier. You just add another entry to the dictionary.</p>
<h2 id="heading-factory-pattern-with-parameters">Factory Pattern with Parameters</h2>
<p>Real-world objects often need configuration. Let's extend our factory to handle notifiers that require initialization parameters.</p>
<p>We'll create a document generator that produces different file formats with custom settings:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PDFDocument</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, title, author</span>):</span>
        self.title = title
        self.author = author
        self.format = <span class="hljs-string">"PDF"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Generating <span class="hljs-subst">{self.format}</span>: '<span class="hljs-subst">{self.title}</span>' by <span class="hljs-subst">{self.author}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WordDocument</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, title, author</span>):</span>
        self.title = title
        self.author = author
        self.format = <span class="hljs-string">"DOCX"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Generating <span class="hljs-subst">{self.format}</span>: '<span class="hljs-subst">{self.title}</span>' by <span class="hljs-subst">{self.author}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MarkdownDocument</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, title, author</span>):</span>
        self.title = title
        self.author = author
        self.format = <span class="hljs-string">"MD"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Generating <span class="hljs-subst">{self.format}</span>: '<span class="hljs-subst">{self.title}</span>' by <span class="hljs-subst">{self.author}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DocumentFactory</span>:</span>
    document_types = {
        <span class="hljs-string">"pdf"</span>: PDFDocument,
        <span class="hljs-string">"word"</span>: WordDocument,
        <span class="hljs-string">"markdown"</span>: MarkdownDocument
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_document</span>(<span class="hljs-params">doc_type, title, author</span>):</span>
        document_class = DocumentFactory.document_types.get(doc_type)
        <span class="hljs-keyword">if</span> document_class:
            <span class="hljs-keyword">return</span> document_class(title, author)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown document type: <span class="hljs-subst">{doc_type}</span>"</span>)
</code></pre>
<p>The key difference here is that our factory method now accepts additional parameters.</p>
<p>The <code>create_document</code> method takes <code>doc_type</code>, <code>title</code>, and <code>author</code> as arguments. When we instantiate the class, we pass the <code>title</code> and <code>author</code> to the <code>create_document</code> constructor: <code>document_class(title, author)</code>.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create different documents with parameters</span>
pdf = DocumentFactory.create_document(<span class="hljs-string">"pdf"</span>, <span class="hljs-string">"Python Guide"</span>, <span class="hljs-string">"Tutorial Team"</span>)
word = DocumentFactory.create_document(<span class="hljs-string">"word"</span>, <span class="hljs-string">"Meeting Notes"</span>, <span class="hljs-string">"Grace Dev"</span>)
markdown = DocumentFactory.create_document(<span class="hljs-string">"markdown"</span>, <span class="hljs-string">"README"</span>, <span class="hljs-string">"DevTeam"</span>)
</code></pre>
<p>This lets us create fully configured objects through the factory while keeping the creation logic centralized.</p>
<h2 id="heading-using-abstract-base-classes">Using Abstract Base Classes</h2>
<p>To make our factory more robust, we can use Python's <a target="_blank" href="https://docs.python.org/3/library/abc.html">Abstract Base Classes (ABC)</a> to enforce a common interface.</p>
<p>Let's create a super simple payment processing system:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> abc <span class="hljs-keyword">import</span> ABC, abstractmethod

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentProcessor</span>(<span class="hljs-params">ABC</span>):</span>
<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_payment</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">pass</span>

<span class="hljs-meta">    @abstractmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refund</span>(<span class="hljs-params">self, transaction_id</span>):</span>
        <span class="hljs-keyword">pass</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreditCardProcessor</span>(<span class="hljs-params">PaymentProcessor</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_payment</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Processing $<span class="hljs-subst">{amount}</span> via Credit Card"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refund</span>(<span class="hljs-params">self, transaction_id</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Refunding credit card transaction <span class="hljs-subst">{transaction_id}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PayPalProcessor</span>(<span class="hljs-params">PaymentProcessor</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_payment</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Processing $<span class="hljs-subst">{amount}</span> via PayPal"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refund</span>(<span class="hljs-params">self, transaction_id</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Refunding PayPal transaction <span class="hljs-subst">{transaction_id}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentFactory</span>:</span>
    processors = {
        <span class="hljs-string">"credit_card"</span>: CreditCardProcessor,
        <span class="hljs-string">"paypal"</span>: PayPalProcessor
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_processor</span>(<span class="hljs-params">processor_type</span>):</span>
        processor_class = PaymentFactory.processors.get(processor_type)
        <span class="hljs-keyword">if</span> processor_class:
            <span class="hljs-keyword">return</span> processor_class()
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown processor type: <span class="hljs-subst">{processor_type}</span>"</span>)
</code></pre>
<p>Here, the <code>PaymentProcessor</code> class defines an interface that <em>all</em> payment processors must implement. The <code>@abstractmethod</code> decorator marks methods that subclasses must override.</p>
<p>You cannot instantiate <code>PaymentProcessor</code> directly. It only serves as a blueprint. All concrete processors (<code>CreditCardProcessor</code>, <code>PayPalProcessor</code>) must implement both <code>process_payment</code> and <code>refund</code> methods. If they don't, Python will raise an error. This guarantees that any object created by our factory will have the expected methods, making our code more predictable and safer.</p>
<p>You can use the factory like so:</p>
<pre><code class="lang-python">processor = PaymentFactory.create_processor(<span class="hljs-string">"paypal"</span>)
</code></pre>
<h2 id="heading-a-more-helpful-example-database-connection-factory">A More Helpful Example: Database Connection Factory</h2>
<p>Let's build something practical: a factory that creates different database connection objects based on configuration.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MySQLConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, host, database</span>):</span>
        self.host = host
        self.database = database
        self.connection_type = <span class="hljs-string">"MySQL"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Connected to <span class="hljs-subst">{self.connection_type}</span> at <span class="hljs-subst">{self.host}</span>/<span class="hljs-subst">{self.database}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute_query</span>(<span class="hljs-params">self, query</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing on MySQL: <span class="hljs-subst">{query}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PostgreSQLConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, host, database</span>):</span>
        self.host = host
        self.database = database
        self.connection_type = <span class="hljs-string">"PostgreSQL"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Connected to <span class="hljs-subst">{self.connection_type}</span> at <span class="hljs-subst">{self.host}</span>/<span class="hljs-subst">{self.database}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute_query</span>(<span class="hljs-params">self, query</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing on PostgreSQL: <span class="hljs-subst">{query}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SQLiteConnection</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, host, database</span>):</span>
        self.host = host
        self.database = database
        self.connection_type = <span class="hljs-string">"SQLite"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Connected to <span class="hljs-subst">{self.connection_type}</span> at <span class="hljs-subst">{self.host}</span>/<span class="hljs-subst">{self.database}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute_query</span>(<span class="hljs-params">self, query</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing on SQLite: <span class="hljs-subst">{query}</span>"</span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseFactory</span>:</span>
    db_types = {
        <span class="hljs-string">"mysql"</span>: MySQLConnection,
        <span class="hljs-string">"postgresql"</span>: PostgreSQLConnection,
        <span class="hljs-string">"sqlite"</span>: SQLiteConnection
    }

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_connection</span>(<span class="hljs-params">db_type, host, database</span>):</span>
        db_class = DatabaseFactory.db_types.get(db_type)
        <span class="hljs-keyword">if</span> db_class:
            <span class="hljs-keyword">return</span> db_class(host, database)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Unknown database type: <span class="hljs-subst">{db_type}</span>"</span>)

<span class="hljs-meta">    @staticmethod</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_from_config</span>(<span class="hljs-params">config</span>):</span>
        <span class="hljs-string">"""Create a database connection from a configuration dictionary"""</span>
        <span class="hljs-keyword">return</span> DatabaseFactory.create_connection(
            config[<span class="hljs-string">"type"</span>],
            config[<span class="hljs-string">"host"</span>],
            config[<span class="hljs-string">"database"</span>]
        )
</code></pre>
<p>This example shows a more realistic use case. We have multiple database connection classes, each with the same interface but different implementations.</p>
<p>The factory has two creation methods: <code>create_connection</code> for direct parameters and <code>create_from_config</code> for configuration dictionaries.</p>
<p>The <code>create_from_config</code> method is particularly useful because it lets you load database settings from a config file or environment variables and create the appropriate connection object.</p>
<p>This pattern makes it easy to switch between different databases without changing your application code. You just change the configuration as shown:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Use with direct parameters</span>
db1 = DatabaseFactory.create_connection(<span class="hljs-string">"mysql"</span>, <span class="hljs-string">"localhost"</span>, <span class="hljs-string">"myapp_db"</span>)
print(db1.connect())
print(db1.execute_query(<span class="hljs-string">"SELECT * FROM users"</span>))

<span class="hljs-comment"># Use with configuration dictionary</span>
config = {
    <span class="hljs-string">"type"</span>: <span class="hljs-string">"postgresql"</span>,
    <span class="hljs-string">"host"</span>: <span class="hljs-string">"db.example.com"</span>,
    <span class="hljs-string">"database"</span>: <span class="hljs-string">"production_db"</span>
}
db2 = DatabaseFactory.create_from_config(config)
</code></pre>
<h2 id="heading-when-to-use-the-factory-pattern">When to Use the Factory Pattern</h2>
<p>The factory pattern is useful when you have the following:</p>
<ol>
<li><p><strong>Multiple related classes</strong>: When you have several classes that share a common interface but have different implementations (like the payment processors or database connections we had in the examples).</p>
</li>
<li><p><strong>Runtime decisions</strong>: When you need to decide which class to instantiate based on user input, configuration, or other runtime conditions.</p>
</li>
<li><p><strong>Complex object creation</strong>: When creating an object involves multiple steps or requires specific logic that you want to encapsulate.</p>
</li>
</ol>
<p>However, don't use the factory pattern when:</p>
<ul>
<li><p>You only have one or two simple classes</p>
</li>
<li><p>Object creation is straightforward with no special logic</p>
</li>
<li><p>The added abstraction makes your code harder to understand</p>
</li>
</ul>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>The factory pattern is a useful tool for managing object creation in Python. It helps you write cleaner, more maintainable code by centralizing creation logic and decoupling your code from specific class implementations. We've covered:</p>
<ul>
<li><p>Basic factory implementation with simple examples</p>
</li>
<li><p>Using dictionaries for cleaner factory code</p>
</li>
<li><p>Passing parameters to factory-created objects</p>
</li>
<li><p>Using abstract base classes for cleaner interfaces</p>
</li>
</ul>
<p>The key takeaway is this: <strong>whenever you find yourself writing repetitive object creation code or need to decide which class to instantiate at runtime, consider using the factory pattern</strong>. Start simple and add complexity only when needed. The basic dictionary-based factory is often all you need for most applications.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use the Builder Pattern in Python – A Practical Guide for Developers ]]>
                </title>
                <description>
                    <![CDATA[ Creating complex objects can get messy. You've probably written constructors with too many parameters, struggled with optional arguments, or created objects that require multiple setup steps. The builder pattern solves these problems by separating ob... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-the-builder-pattern-in-python-a-practical-guide-for-devs/</link>
                <guid isPermaLink="false">697a3bb24b19a21e89e9cf78</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 28 Jan 2026 16:39:14 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769618329398/ad5581ff-5670-4d55-b269-6f0afcb7c57c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Creating complex objects can get messy. You've probably written constructors with too many parameters, struggled with optional arguments, or created objects that require multiple setup steps. The builder pattern solves these problems by separating object construction from representation.</p>
<p>In this tutorial, I'll show you how to implement the builder pattern in Python. I’ll also explain when it's useful, and show practical examples you can use in your projects.</p>
<p>You can find the code <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/builder">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p>Python 3.10 or higher installed</p>
</li>
<li><p>Understanding of Python classes and methods</p>
</li>
<li><p>Familiarity with <a target="_blank" href="https://www.youtube.com/watch?v=Ej_02ICOIgs">object-oriented programming</a> (OOP) concepts</p>
</li>
</ul>
<p>Let’s get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-understanding-the-builder-pattern">Understanding the Builder Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-problem-complex-object-construction">The Problem: Complex Object Construction</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-basic-builder-pattern-implementation">Basic Builder Pattern Implementation</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-more-helpful-example-sql-query-builder">A More Helpful Example: SQL Query Builder</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-validation-and-error-handling">Validation and Error Handling</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-pythonic-builder-pattern">The Pythonic Builder Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-the-builder-pattern">When to Use the Builder Pattern</a></p>
</li>
</ol>
<h2 id="heading-understanding-the-builder-pattern">Understanding the Builder Pattern</h2>
<p>The builder pattern addresses the problem of constructing complex objects. <strong>Instead of cramming all construction logic into a constructor, you create a separate builder class that constructs the object incrementally</strong>.</p>
<p>Consider building a SQL query. A simple query might be <code>SELECT * FROM users</code>, but most queries have <code>WHERE</code> clauses, <code>JOIN</code>s, <code>ORDER BY</code>, <code>GROUP BY</code>, and <code>LIMIT</code> clauses. You <em>could</em> pass all these as constructor parameters, but that becomes unwieldy fast. The builder pattern lets you construct the query piece by piece.</p>
<p>The pattern separates two concerns: <strong>what the final object should be (the product) and how to build it (the builder)</strong>. This separation gives you flexibility because you can now have multiple builders that create the same type of object in different ways, or one builder that creates different variations.</p>
<p>Python is simpler and more flexible to code in, which means we can implement builders more elegantly than in languages like Java or C++. We'll explore both traditional and Pythonic approaches.</p>
<h2 id="heading-the-problem-complex-object-construction">The Problem: Complex Object Construction</h2>
<p>Let's start with a problem that shows why builders are useful. We'll create an HTTP request configuration – something complex enough to show the pattern's value without being overwhelming.</p>
<pre><code class="lang-python"><span class="hljs-comment"># The naive approach - constructor with many parameters</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HTTPRequest</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, url, method=<span class="hljs-string">"GET"</span>, headers=None, body=None, 
                 timeout=<span class="hljs-number">30</span>, auth=None, verify_ssl=True, allow_redirects=True,
                 max_redirects=<span class="hljs-number">5</span>, cookies=None, proxies=None</span>):</span>
        self.url = url
        self.method = method
        self.headers = headers <span class="hljs-keyword">or</span> {}
        self.body = body
        self.timeout = timeout
        self.auth = auth
        self.verify_ssl = verify_ssl
        self.allow_redirects = allow_redirects
        self.max_redirects = max_redirects
        self.cookies = cookies <span class="hljs-keyword">or</span> {}
        self.proxies = proxies <span class="hljs-keyword">or</span> {}

<span class="hljs-comment"># Using it is messy</span>
request = HTTPRequest(
    <span class="hljs-string">"https://api.example.com/users"</span>,
    method=<span class="hljs-string">"POST"</span>,
    headers={<span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>},
    body=<span class="hljs-string">'{"name": "John"}'</span>,
    timeout=<span class="hljs-number">60</span>,
    auth=(<span class="hljs-string">"username"</span>, <span class="hljs-string">"password"</span>),
    verify_ssl=<span class="hljs-literal">True</span>,
    allow_redirects=<span class="hljs-literal">False</span>,
    max_redirects=<span class="hljs-number">0</span>,
    cookies={<span class="hljs-string">"session"</span>: <span class="hljs-string">"abc123"</span>},
    proxies={<span class="hljs-string">"http"</span>: <span class="hljs-string">"proxy.example.com"</span>}
)

print(<span class="hljs-string">f"Request to: <span class="hljs-subst">{request.url}</span>"</span>)
print(<span class="hljs-string">f"Method: <span class="hljs-subst">{request.method}</span>"</span>)
print(<span class="hljs-string">f"Timeout: <span class="hljs-subst">{request.timeout}</span>s"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Request to: https://api.example.com/users
Method: POST
Timeout: 60s
</code></pre>
<p>This constructor is hard to use. You need to remember parameter order, pass <code>None</code> for things you don't want, and it's unclear what the defaults are. When creating the request, you can't tell which parameters are required without checking the documentation. This is where the builder pattern comes in handy.</p>
<h2 id="heading-basic-builder-pattern-implementation">Basic Builder Pattern Implementation</h2>
<p>Let's rebuild this using the builder pattern. The builder provides methods for setting each property, making construction explicit and readable.</p>
<p>First, we define the product class, which is the object we want to build:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HTTPRequest</span>:</span>
    <span class="hljs-string">"""The product - what we're building"""</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, url</span>):</span>
        self.url = url
        self.method = <span class="hljs-string">"GET"</span>
        self.headers = {}
        self.body = <span class="hljs-literal">None</span>
        self.timeout = <span class="hljs-number">30</span>
        self.auth = <span class="hljs-literal">None</span>
        self.verify_ssl = <span class="hljs-literal">True</span>
        self.allow_redirects = <span class="hljs-literal">True</span>
        self.max_redirects = <span class="hljs-number">5</span>
        self.cookies = {}
        self.proxies = {}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">execute</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Simulate executing the request"""</span>
        auth_str = <span class="hljs-string">f" (auth: <span class="hljs-subst">{self.auth[<span class="hljs-number">0</span>]}</span>)"</span> <span class="hljs-keyword">if</span> self.auth <span class="hljs-keyword">else</span> <span class="hljs-string">""</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"<span class="hljs-subst">{self.method}</span> <span class="hljs-subst">{self.url}</span><span class="hljs-subst">{auth_str}</span> - timeout: <span class="hljs-subst">{self.timeout}</span>s"</span>
</code></pre>
<p>Now we create the builder class. Each method modifies the request and returns self to enable method chaining:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HTTPRequestBuilder</span>:</span>
    <span class="hljs-string">"""The builder - constructs HTTPRequest step by step"""</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, url</span>):</span>
        self._request = HTTPRequest(url)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">method</span>(<span class="hljs-params">self, method</span>):</span>
        <span class="hljs-string">"""Set HTTP method (GET, POST, etc.)"""</span>
        self._request.method = method.upper()
        <span class="hljs-keyword">return</span> self  <span class="hljs-comment"># Return self for method chaining</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">header</span>(<span class="hljs-params">self, key, value</span>):</span>
        <span class="hljs-string">"""Add a header"""</span>
        self._request.headers[key] = value
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">headers</span>(<span class="hljs-params">self, headers_dict</span>):</span>
        <span class="hljs-string">"""Add multiple headers at once"""</span>
        self._request.headers.update(headers_dict)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">body</span>(<span class="hljs-params">self, body</span>):</span>
        <span class="hljs-string">"""Set request body"""</span>
        self._request.body = body
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">timeout</span>(<span class="hljs-params">self, seconds</span>):</span>
        <span class="hljs-string">"""Set timeout in seconds"""</span>
        self._request.timeout = seconds
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">auth</span>(<span class="hljs-params">self, username, password</span>):</span>
        <span class="hljs-string">"""Set basic authentication"""</span>
        self._request.auth = (username, password)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">disable_ssl_verification</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Disable SSL certificate verification"""</span>
        self._request.verify_ssl = <span class="hljs-literal">False</span>
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">disable_redirects</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Disable automatic redirects"""</span>
        self._request.allow_redirects = <span class="hljs-literal">False</span>
        self._request.max_redirects = <span class="hljs-number">0</span>
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">build</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Return the constructed request"""</span>
        <span class="hljs-keyword">return</span> self._request
</code></pre>
<p>Now let's use the builder to create a request:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Now using the builder is much cleaner and more readable</span>
request = (HTTPRequestBuilder(<span class="hljs-string">"https://api.example.com/users"</span>)
    .method(<span class="hljs-string">"POST"</span>)
    .header(<span class="hljs-string">"Content-Type"</span>, <span class="hljs-string">"application/json"</span>)
    .header(<span class="hljs-string">"Accept"</span>, <span class="hljs-string">"application/json"</span>)
    .body(<span class="hljs-string">'{"name": "John", "email": "john@example.com"}'</span>)
    .timeout(<span class="hljs-number">60</span>)
    .auth(<span class="hljs-string">"username"</span>, <span class="hljs-string">"password"</span>)
    .disable_redirects()
    .build())

print(request.execute())
print(<span class="hljs-string">f"\nHeaders: <span class="hljs-subst">{request.headers}</span>"</span>)
print(<span class="hljs-string">f"SSL verification: <span class="hljs-subst">{request.verify_ssl}</span>"</span>)
print(<span class="hljs-string">f"Allow redirects: <span class="hljs-subst">{request.allow_redirects}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">
Headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}
SSL verification: True
Allow redirects: False
</code></pre>
<p>The builder makes construction much clearer. Each method describes what it does, and method chaining creates a fluent interface that reads almost like English. You only specify what you need – everything else gets sensible defaults. The construction process is explicit and self-documenting.</p>
<p>Notice that each builder method returns <code>self</code>. This enables method chaining where you can call multiple methods in sequence. The final <code>build()</code> method returns the constructed object. This separation between building and the final product is the core of the pattern.</p>
<h2 id="heading-a-more-helpful-example-sql-query-builder">A More Helpful Example: SQL Query Builder</h2>
<p>Let's build something more useful and helps us understand how the pattern works: a SQL query builder. This is a practical tool you might actually use in projects.</p>
<p>First, we define the SQL query product class:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SQLQuery</span>:</span>
    <span class="hljs-string">"""The product - represents a SQL query"""</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.select_columns = []
        self.from_table = <span class="hljs-literal">None</span>
        self.joins = []
        self.where_conditions = []
        self.group_by_columns = []
        self.having_conditions = []
        self.order_by_columns = []
        self.limit_value = <span class="hljs-literal">None</span>
        self.offset_value = <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">to_sql</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Convert the query object to SQL string"""</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self.from_table:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"FROM clause is required"</span>)

        <span class="hljs-comment"># Build SELECT clause</span>
        columns = <span class="hljs-string">", "</span>.join(self.select_columns) <span class="hljs-keyword">if</span> self.select_columns <span class="hljs-keyword">else</span> <span class="hljs-string">"*"</span>
        sql = <span class="hljs-string">f"SELECT <span class="hljs-subst">{columns}</span>"</span>

        <span class="hljs-comment"># Add FROM clause</span>
        sql += <span class="hljs-string">f"\nFROM <span class="hljs-subst">{self.from_table}</span>"</span>

        <span class="hljs-comment"># Add JOINs</span>
        <span class="hljs-keyword">for</span> join <span class="hljs-keyword">in</span> self.joins:
            sql += <span class="hljs-string">f"\n<span class="hljs-subst">{join}</span>"</span>

        <span class="hljs-comment"># Add WHERE clause</span>
        <span class="hljs-keyword">if</span> self.where_conditions:
            conditions = <span class="hljs-string">" AND "</span>.join(self.where_conditions)
            sql += <span class="hljs-string">f"\nWHERE <span class="hljs-subst">{conditions}</span>"</span>

        <span class="hljs-comment"># Add GROUP BY</span>
        <span class="hljs-keyword">if</span> self.group_by_columns:
            columns = <span class="hljs-string">", "</span>.join(self.group_by_columns)
            sql += <span class="hljs-string">f"\nGROUP BY <span class="hljs-subst">{columns}</span>"</span>

        <span class="hljs-comment"># Add HAVING</span>
        <span class="hljs-keyword">if</span> self.having_conditions:
            conditions = <span class="hljs-string">" AND "</span>.join(self.having_conditions)
            sql += <span class="hljs-string">f"\nHAVING <span class="hljs-subst">{conditions}</span>"</span>

        <span class="hljs-comment"># Add ORDER BY</span>
        <span class="hljs-keyword">if</span> self.order_by_columns:
            columns = <span class="hljs-string">", "</span>.join(self.order_by_columns)
            sql += <span class="hljs-string">f"\nORDER BY <span class="hljs-subst">{columns}</span>"</span>

        <span class="hljs-comment"># Add LIMIT and OFFSET</span>
        <span class="hljs-keyword">if</span> self.limit_value:
            sql += <span class="hljs-string">f"\nLIMIT <span class="hljs-subst">{self.limit_value}</span>"</span>
        <span class="hljs-keyword">if</span> self.offset_value:
            sql += <span class="hljs-string">f"\nOFFSET <span class="hljs-subst">{self.offset_value}</span>"</span>

        <span class="hljs-keyword">return</span> sql
</code></pre>
<p>Now we create the query builder with methods for each SQL clause:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">QueryBuilder</span>:</span>
    <span class="hljs-string">"""Builder for SQL queries"""</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._query = SQLQuery()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">select</span>(<span class="hljs-params">self, *columns</span>):</span>
        <span class="hljs-string">"""Add columns to SELECT clause"""</span>
        self._query.select_columns.extend(columns)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">from_table</span>(<span class="hljs-params">self, table</span>):</span>
        <span class="hljs-string">"""Set the FROM table"""</span>
        self._query.from_table = table
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">join</span>(<span class="hljs-params">self, table, on_condition, join_type=<span class="hljs-string">"INNER"</span></span>):</span>
        <span class="hljs-string">"""Add a JOIN clause"""</span>
        join_clause = <span class="hljs-string">f"<span class="hljs-subst">{join_type}</span> JOIN <span class="hljs-subst">{table}</span> ON <span class="hljs-subst">{on_condition}</span>"</span>
        self._query.joins.append(join_clause)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">left_join</span>(<span class="hljs-params">self, table, on_condition</span>):</span>
        <span class="hljs-string">"""Convenience method for LEFT JOIN"""</span>
        <span class="hljs-keyword">return</span> self.join(table, on_condition, <span class="hljs-string">"LEFT"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">where</span>(<span class="hljs-params">self, condition</span>):</span>
        <span class="hljs-string">"""Add a WHERE condition"""</span>
        self._query.where_conditions.append(condition)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">group_by</span>(<span class="hljs-params">self, *columns</span>):</span>
        <span class="hljs-string">"""Add GROUP BY columns"""</span>
        self._query.group_by_columns.extend(columns)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">having</span>(<span class="hljs-params">self, condition</span>):</span>
        <span class="hljs-string">"""Add a HAVING condition"""</span>
        self._query.having_conditions.append(condition)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">order_by</span>(<span class="hljs-params">self, *columns</span>):</span>
        <span class="hljs-string">"""Add ORDER BY columns"""</span>
        self._query.order_by_columns.extend(columns)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">limit</span>(<span class="hljs-params">self, value</span>):</span>
        <span class="hljs-string">"""Set LIMIT"""</span>
        self._query.limit_value = value
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">offset</span>(<span class="hljs-params">self, value</span>):</span>
        <span class="hljs-string">"""Set OFFSET"""</span>
        self._query.offset_value = value
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">build</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Return the constructed query"""</span>
        <span class="hljs-keyword">return</span> self._query
</code></pre>
<p>Let's use the builder to create queries:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Example 1: Simple query</span>
simple_query = (QueryBuilder()
    .select(<span class="hljs-string">"id"</span>, <span class="hljs-string">"name"</span>, <span class="hljs-string">"email"</span>)
    .from_table(<span class="hljs-string">"users"</span>)
    .where(<span class="hljs-string">"status = 'active'"</span>)
    .order_by(<span class="hljs-string">"name"</span>)
    .limit(<span class="hljs-number">10</span>)
    .build())

print(<span class="hljs-string">"Simple Query:"</span>)
print(simple_query.to_sql())
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Simple Query:
SELECT id, name, email
FROM users
WHERE status = 'active'
ORDER BY name
LIMIT 10
</code></pre>
<p>Now let's create a more complex query with joins and aggregations:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Example 2: Complex query with joins and aggregations</span>
complex_query = (QueryBuilder()
    .select(<span class="hljs-string">"u.name"</span>, <span class="hljs-string">"COUNT(o.id) as order_count"</span>, <span class="hljs-string">"SUM(o.total) as total_spent"</span>)
    .from_table(<span class="hljs-string">"users u"</span>)
    .left_join(<span class="hljs-string">"orders o"</span>, <span class="hljs-string">"u.id = o.user_id"</span>)
    .where(<span class="hljs-string">"u.created_at &gt;= '2024-01-01'"</span>)
    .where(<span class="hljs-string">"u.country = 'US'"</span>)
    .group_by(<span class="hljs-string">"u.id"</span>, <span class="hljs-string">"u.name"</span>)
    .having(<span class="hljs-string">"COUNT(o.id) &gt; 5"</span>)
    .order_by(<span class="hljs-string">"total_spent DESC"</span>)
    .limit(<span class="hljs-number">20</span>)
    .build())

print(<span class="hljs-string">"Complex Query:"</span>)
print(complex_query.to_sql())
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Complex Query:
SELECT u.name, COUNT(o.id) as order_count, SUM(o.total) as total_spent
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at &gt;= '2024-01-01' AND u.country = 'US'
GROUP BY u.id, u.name
HAVING COUNT(o.id) &gt; 5
ORDER BY total_spent DESC
LIMIT 20
</code></pre>
<p>This SQL builder shows that the builder pattern is useful. Building SQL queries programmatically is complex. There are many optional clauses that must appear in a specific order. The builder handles all this complexity, giving you a clean API that prevents errors like putting <code>WHERE</code> after <code>GROUP BY</code>.</p>
<p>The builder ensures you can't create invalid queries (like forgetting the <code>FROM</code> clause) while keeping the API flexible. You can chain methods in any order during construction, and the <code>to_sql()</code> method handles ordering the clauses correctly. This separation of construction from representation is exactly what the builder pattern provides.</p>
<h2 id="heading-validation-and-error-handling">Validation and Error Handling</h2>
<p>Good builders validate data during construction. Let's improve our HTTP request builder with validation.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">HTTPRequestBuilder</span>:</span>
    <span class="hljs-string">"""Enhanced builder with validation"""</span>
    VALID_METHODS = {<span class="hljs-string">"GET"</span>, <span class="hljs-string">"POST"</span>, <span class="hljs-string">"PUT"</span>, <span class="hljs-string">"DELETE"</span>, <span class="hljs-string">"PATCH"</span>, <span class="hljs-string">"HEAD"</span>, <span class="hljs-string">"OPTIONS"</span>}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, url</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> url:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"URL cannot be empty"</span>)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> url.startswith((<span class="hljs-string">"http://"</span>, <span class="hljs-string">"https://"</span>)):
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"URL must start with http:// or https://"</span>)

        self._request = HTTPRequest(url)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">method</span>(<span class="hljs-params">self, method</span>):</span>
        <span class="hljs-string">"""Set HTTP method with validation"""</span>
        method = method.upper()
        <span class="hljs-keyword">if</span> method <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self.VALID_METHODS:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Invalid HTTP method: <span class="hljs-subst">{method}</span>"</span>)
        self._request.method = method
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">timeout</span>(<span class="hljs-params">self, seconds</span>):</span>
        <span class="hljs-string">"""Set timeout with validation"""</span>
        <span class="hljs-keyword">if</span> seconds &lt;= <span class="hljs-number">0</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Timeout must be positive"</span>)
        <span class="hljs-keyword">if</span> seconds &gt; <span class="hljs-number">300</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Timeout cannot exceed 300 seconds"</span>)
        self._request.timeout = seconds
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">header</span>(<span class="hljs-params">self, key, value</span>):</span>
        <span class="hljs-string">"""Add header with validation"""</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> key <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> value:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Header key and value cannot be empty"</span>)
        self._request.headers[key] = value
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">body</span>(<span class="hljs-params">self, body</span>):</span>
        <span class="hljs-string">"""Set request body"""</span>
        self._request.body = body
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">build</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Validate and return the request"""</span>
        <span class="hljs-comment"># Final validation before building</span>
        <span class="hljs-keyword">if</span> self._request.method <span class="hljs-keyword">in</span> {<span class="hljs-string">"POST"</span>, <span class="hljs-string">"PUT"</span>, <span class="hljs-string">"PATCH"</span>} <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> self._request.body:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"<span class="hljs-subst">{self._request.method}</span> requests typically require a body"</span>)

        <span class="hljs-keyword">return</span> self._request
</code></pre>
<p>Now let's test the validation:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Valid request</span>
<span class="hljs-keyword">try</span>:
    valid_request = (HTTPRequestBuilder(<span class="hljs-string">"https://api.example.com/data"</span>)
        .method(<span class="hljs-string">"POST"</span>)
        .body(<span class="hljs-string">'{"key": "value"}'</span>)
        .timeout(<span class="hljs-number">45</span>)
        .build())
    print(<span class="hljs-string">"✓ Valid request created successfully"</span>)
<span class="hljs-keyword">except</span> ValueError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"✗ Error: <span class="hljs-subst">{e}</span>"</span>)

<span class="hljs-comment"># Invalid request - bad method</span>
<span class="hljs-keyword">try</span>:
    invalid_request = (HTTPRequestBuilder(<span class="hljs-string">"https://api.example.com/data"</span>)
        .method(<span class="hljs-string">"INVALID"</span>)
        .build())
<span class="hljs-keyword">except</span> ValueError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"✓ Caught error: <span class="hljs-subst">{e}</span>"</span>)

<span class="hljs-comment"># Invalid request - POST without body</span>
<span class="hljs-keyword">try</span>:
    invalid_request = (HTTPRequestBuilder(<span class="hljs-string">"https://api.example.com/data"</span>)
        .method(<span class="hljs-string">"POST"</span>)
        .build())
<span class="hljs-keyword">except</span> ValueError <span class="hljs-keyword">as</span> e:
    print(<span class="hljs-string">f"✓ Caught error: <span class="hljs-subst">{e}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">✓ Valid request created successfully
✓ Caught error: Invalid HTTP method: INVALID
✓ Caught error: POST requests typically require a body
</code></pre>
<p>Validation in the builder catches errors early, during construction rather than at execution time. This is much better than discovering problems when you try to use the object. The builder becomes a gatekeeper that ensures only valid objects are created.</p>
<p>Each builder method validates its input immediately. The final <code>build()</code> method performs cross-field validation, which checks that require looking at multiple properties together. This layered validation approach catches errors at the most appropriate point.</p>
<h2 id="heading-the-pythonic-builder-pattern">The Pythonic Builder Pattern</h2>
<p>Python's flexibility allows for more concise builder implementations. Here's a Pythonic version using keyword arguments (<code>**kwargs</code>) and context managers.</p>
<p>First, let's define our email message class:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailMessage</span>:</span>
    <span class="hljs-string">"""Email message with builder pattern using kwargs"""</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.to = kwargs.get(<span class="hljs-string">'to'</span>, [])
        self.cc = kwargs.get(<span class="hljs-string">'cc'</span>, [])
        self.bcc = kwargs.get(<span class="hljs-string">'bcc'</span>, [])
        self.subject = kwargs.get(<span class="hljs-string">'subject'</span>, <span class="hljs-string">''</span>)
        self.body = kwargs.get(<span class="hljs-string">'body'</span>, <span class="hljs-string">''</span>)
        self.attachments = kwargs.get(<span class="hljs-string">'attachments'</span>, [])
        self.priority = kwargs.get(<span class="hljs-string">'priority'</span>, <span class="hljs-string">'normal'</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Simulate sending the email"""</span>
        recipients = len(self.to) + len(self.cc) + len(self.bcc)
        attachments = <span class="hljs-string">f" with <span class="hljs-subst">{len(self.attachments)}</span> attachment(s)"</span> <span class="hljs-keyword">if</span> self.attachments <span class="hljs-keyword">else</span> <span class="hljs-string">""</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Sending '<span class="hljs-subst">{self.subject}</span>' to <span class="hljs-subst">{recipients}</span> recipient(s)<span class="hljs-subst">{attachments}</span>"</span>
</code></pre>
<p>Now we create a builder that accumulates parameters:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EmailBuilder</span>:</span>
    <span class="hljs-string">"""Pythonic email builder"""</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._params = {}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">to</span>(<span class="hljs-params">self, *addresses</span>):</span>
        <span class="hljs-string">"""Add TO recipients"""</span>
        self._params.setdefault(<span class="hljs-string">'to'</span>, []).extend(addresses)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">cc</span>(<span class="hljs-params">self, *addresses</span>):</span>
        <span class="hljs-string">"""Add CC recipients"""</span>
        self._params.setdefault(<span class="hljs-string">'cc'</span>, []).extend(addresses)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">subject</span>(<span class="hljs-params">self, subject</span>):</span>
        <span class="hljs-string">"""Set email subject"""</span>
        self._params[<span class="hljs-string">'subject'</span>] = subject
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">body</span>(<span class="hljs-params">self, body</span>):</span>
        <span class="hljs-string">"""Set email body"""</span>
        self._params[<span class="hljs-string">'body'</span>] = body
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">attach</span>(<span class="hljs-params">self, *files</span>):</span>
        <span class="hljs-string">"""Attach files"""</span>
        self._params.setdefault(<span class="hljs-string">'attachments'</span>, []).extend(files)
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">priority</span>(<span class="hljs-params">self, level</span>):</span>
        <span class="hljs-string">"""Set priority (low, normal, high)"""</span>
        <span class="hljs-keyword">if</span> level <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> (<span class="hljs-string">'low'</span>, <span class="hljs-string">'normal'</span>, <span class="hljs-string">'high'</span>):
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Priority must be low, normal, or high"</span>)
        self._params[<span class="hljs-string">'priority'</span>] = level
        <span class="hljs-keyword">return</span> self

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">build</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""Build the email message"""</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self._params.get(<span class="hljs-string">'to'</span>):
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"At least one recipient is required"</span>)
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self._params.get(<span class="hljs-string">'subject'</span>):
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Subject is required"</span>)

        <span class="hljs-keyword">return</span> EmailMessage(**self._params)
</code></pre>
<p>Let's use it to build and send an email:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Build and send an email</span>
email = (EmailBuilder()
    .to(<span class="hljs-string">"alice@example.com"</span>, <span class="hljs-string">"bob@example.com"</span>)
    .cc(<span class="hljs-string">"manager@example.com"</span>)
    .subject(<span class="hljs-string">"Q4 Sales Report"</span>)
    .body(<span class="hljs-string">"Please find the Q4 sales report attached."</span>)
    .attach(<span class="hljs-string">"q4_report.pdf"</span>, <span class="hljs-string">"sales_data.xlsx"</span>)
    .priority(<span class="hljs-string">"high"</span>)
    .build())

print(email.send())
print(<span class="hljs-string">f"To: <span class="hljs-subst">{email.to}</span>"</span>)
print(<span class="hljs-string">f"CC: <span class="hljs-subst">{email.cc}</span>"</span>)
print(<span class="hljs-string">f"Priority: <span class="hljs-subst">{email.priority}</span>"</span>)
print(<span class="hljs-string">f"Attachments: <span class="hljs-subst">{email.attachments}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Sending 'Q4 Sales Report' to 3 recipient(s) with 2 attachment(s)
To: ['alice@example.com', 'bob@example.com']
CC: ['manager@example.com']
Priority: high
Attachments: ['q4_report.pdf', 'sales_data.xlsx']
</code></pre>
<p>This Pythonic version uses <code>**kwargs</code> to pass parameters to the product, making the builder more flexible. The builder accumulates parameters in a dictionary and passes them all at once during <code>build()</code>. This approach is cleaner for Python.</p>
<p>The key here is that Python doesn't require the boilerplate code that other languages need. We can achieve the same benefits with less boilerplate while still maintaining the builder pattern's core advantages: readable construction, validation, and separation of concerns.</p>
<h2 id="heading-when-to-use-the-builder-pattern">When to Use the Builder Pattern</h2>
<p>The builder pattern is useful in specific situations. Understanding when to use it helps you avoid overengineering.</p>
<p>Use builder pattern when:</p>
<ul>
<li><p>You're creating objects with many optional parameters. If your constructor has more than 3-4 parameters, especially if many are optional, consider a builder. The pattern makes construction explicit and self-documenting.</p>
</li>
<li><p>Object construction requires multiple steps or specific ordering. If you need to set up an object through several method calls in a particular sequence, a builder can enforce and simplify this process.</p>
</li>
<li><p>You need to create different variations of an object. Builders can create different representations of the same type, like different SQL query types or different HTTP request configurations.</p>
</li>
</ul>
<p>However, don't use builders when:</p>
<ul>
<li><p>Your objects are simple. If a regular constructor with 2-3 parameters works fine, don't add builder complexity. Python's keyword arguments already make simple construction readable.</p>
</li>
<li><p>You're only setting attributes. Python objects can have attributes set directly. If there's no validation or complex construction logic, a builder adds unnecessary complexity.</p>
</li>
</ul>
<p>The pattern is useful for complex configuration objects, query builders, document generators, or any object that requires careful step-by-step construction. For simple data containers, stick with straightforward constructors.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you found this tutorial useful. The builder pattern separates object construction from representation, making complex objects easier to create and maintain. You've learned how to implement builders in Python, from traditional approaches to more Pythonic variants using the language's dynamic features.</p>
<p>Remember that the builder pattern is a tool, not a requirement. Use it when construction is genuinely complex and the pattern adds clarity. For simple objects, Python's flexibility provides simpler solutions. Choose the right tool for your specific problem, and you'll write clearer, more maintainable code.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Singleton in Python (and Why You Probably Shouldn't) ]]>
                </title>
                <description>
                    <![CDATA[ The singleton pattern ensures that a class has exactly one instance throughout your application. You've probably seen it in configuration managers, database connections, or logging systems. While singletons seem useful, they often create more problem... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-singleton-in-python-and-why-you-probably-shouldnt/</link>
                <guid isPermaLink="false">697268cdbe089d77317d8295</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Thu, 22 Jan 2026 18:13:33 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769105598620/fad4c5d3-1633-44be-bd40-246f3eb14a97.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The <a target="_blank" href="https://refactoring.guru/design-patterns/singleton">singleton pattern</a> ensures that a class has exactly one instance throughout your application. You've probably seen it in configuration managers, database connections, or logging systems. While singletons seem useful, they often create more problems than they solve.</p>
<p>In this tutorial, I'll show you how to implement singletons in Python, explain when they might be appropriate, and discuss better alternatives for most use cases.</p>
<p>You can find the code <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/design-patterns/singleton">on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start, make sure you have:</p>
<ul>
<li><p><strong>Python 3.10 or higher</strong> installed</p>
</li>
<li><p>Understanding of Python classes and decorators</p>
</li>
<li><p>Familiarity with object-oriented programming concepts</p>
</li>
</ul>
<p>No external libraries needed as we'll use only Python's standard library.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-a-singleton">What is a Singleton?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-classic-singleton-pattern">The Classic Singleton Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-decorator-pattern">The Decorator Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-the-metaclass-approach">The Metaclass Approach</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-thread-safe-singleton">Thread-Safe Singleton</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-you-probably-shouldnt-use-singletons">Why You Probably Shouldn't Use Singletons</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-better-alternatives-to-the-singleton-pattern">Better Alternatives to the Singleton Pattern</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-singletons-are-acceptable">When Singletons Are Acceptable</a></p>
</li>
</ol>
<h2 id="heading-what-is-a-singleton">What Is a Singleton?</h2>
<p><strong>A singleton is a design pattern that restricts a class to a single instance</strong>. No matter how many times you try to create an object from that class, you always get the same instance back.</p>
<p>The classic use case is a configuration object. You want all parts of your application to share the same configuration, not create separate copies. Instead of passing the config object everywhere, the singleton pattern lets you access it globally.</p>
<p>Here's the problem: global state is problematic. When any part of your code can modify shared state, debugging becomes difficult. You lose the ability to reason about code in isolation. Tests become harder because they share state between runs.</p>
<p>Despite these issues, there are a few genuine use cases. Let's explore how to build singletons properly, then discuss when you actually need them.</p>
<h2 id="heading-the-classic-singleton-pattern">The Classic Singleton Pattern</h2>
<p>The traditional approach uses a class variable to store the single instance. When you try to create a new instance, the class checks if one already exists.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DatabaseConnection</span>:</span>
    <span class="hljs-string">"""
    Classic Singleton pattern using __new__
    """</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</span>):</span>
        <span class="hljs-keyword">if</span> cls._instance <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
            print(<span class="hljs-string">"Creating new database connection"</span>)
            cls._instance = super().__new__(cls)
            cls._instance._initialized = <span class="hljs-literal">False</span>
        <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</span>):</span>
        <span class="hljs-comment"># Only initialize once</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self._initialized:
            print(<span class="hljs-string">"Initializing database connection"</span>)
            self.connection_string = <span class="hljs-string">"postgresql://localhost/mydb"</span>
            self.pool_size = <span class="hljs-number">10</span>
            self._initialized = <span class="hljs-literal">True</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">query</span>(<span class="hljs-params">self, sql</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing: <span class="hljs-subst">{sql}</span>"</span>
</code></pre>
<p>Let’s now test the singleton behavior:</p>
<pre><code class="lang-python">db1 = DatabaseConnection()
print(<span class="hljs-string">f"db1 connection: <span class="hljs-subst">{db1.connection_string}</span>"</span>)

print(<span class="hljs-string">"\nCreating second instance:"</span>)
db2 = DatabaseConnection()
print(<span class="hljs-string">f"db2 connection: <span class="hljs-subst">{db2.connection_string}</span>"</span>)

print(<span class="hljs-string">f"\nAre they the same object? <span class="hljs-subst">{db1 <span class="hljs-keyword">is</span> db2}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Creating new database connection
Initializing database connection
db1 connection: postgresql://localhost/mydb

Creating second instance:
Are they the same object? True
</code></pre>
<p>The <code>__new__</code> method controls object creation in Python. By overriding it, we intercept instance creation and return our stored instance if it exists. The <code>__init__</code> method still runs each time, so we add an <code>_initialized</code> flag to prevent re-initialization.</p>
<p>This pattern works, but it's verbose and easy to mess up. The <code>_initialized</code> flag feels like a hack. Let's look at cleaner approaches.</p>
<h2 id="heading-the-decorator-pattern">The Decorator Pattern</h2>
<p>A more Pythonic approach uses a decorator to handle the singleton logic. This keeps the class clean and moves the singleton behavior to a reusable decorator.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">singleton</span>(<span class="hljs-params">cls</span>):</span>
    <span class="hljs-string">"""
    Decorator that converts a class into a singleton
    """</span>
    instances = {}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_instance</span>(<span class="hljs-params">*args, **kwargs</span>):</span>
        <span class="hljs-keyword">if</span> cls <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> instances:
            instances[cls] = cls(*args, **kwargs)
        <span class="hljs-keyword">return</span> instances[cls]

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

<span class="hljs-meta">@singleton</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppConfig</span>:</span>
    <span class="hljs-string">"""
    Application configuration as a singleton
    """</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        print(<span class="hljs-string">"Loading configuration..."</span>)
        self.debug_mode = <span class="hljs-literal">True</span>
        self.api_key = <span class="hljs-string">"secret-key-12345"</span>
        self.max_connections = <span class="hljs-number">100</span>
        self.timeout = <span class="hljs-number">30</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_setting</span>(<span class="hljs-params">self, key, value</span>):</span>
        setattr(self, key, value)
        print(<span class="hljs-string">f"Updated <span class="hljs-subst">{key}</span> = <span class="hljs-subst">{value}</span>"</span>)
</code></pre>
<p>As with the earlier approach, let’s test the decorator approach:</p>
<pre><code class="lang-python"><span class="hljs-comment"># First access</span>
config1 = AppConfig()
print(<span class="hljs-string">f"Debug mode: <span class="hljs-subst">{config1.debug_mode}</span>"</span>)

<span class="hljs-comment"># Second access - no re-initialization</span>
print(<span class="hljs-string">"\nAccessing config again:"</span>)
config2 = AppConfig()
config2.update_setting(<span class="hljs-string">"timeout"</span>, <span class="hljs-number">60</span>)

print(<span class="hljs-string">f"\nconfig1 timeout: <span class="hljs-subst">{config1.timeout}</span>"</span>)
print(<span class="hljs-string">f"Same instance? <span class="hljs-subst">{config1 <span class="hljs-keyword">is</span> config2}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Loading configuration...
Debug mode: True

Accessing config again:
Updated timeout = 60

config1 timeout: 60
Same instance? True
</code></pre>
<p>The decorator pattern is cleaner. The <code>@singleton</code> decorator wraps the class and maintains instances in a closure. This keeps singleton logic separate from the class implementation. The class itself remains simple and testable.</p>
<p>Notice how modifying <code>config2</code> affects <code>config1</code> as they're the same object. This shared state can be useful but also dangerous. Any code that gets the config can modify it, potentially breaking other parts of your application.</p>
<h2 id="heading-the-metaclass-approach">The Metaclass Approach</h2>
<p>For more control, you can use a metaclass. Metaclasses control class creation itself, making them a natural fit for singletons.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SingletonMeta</span>(<span class="hljs-params">type</span>):</span>
    <span class="hljs-string">"""
    Metaclass that creates singleton instances
    """</span>
    _instances = {}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__call__</span>(<span class="hljs-params">cls, *args, **kwargs</span>):</span>
        <span class="hljs-keyword">if</span> cls <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        <span class="hljs-keyword">return</span> cls._instances[cls]

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Logger</span>(<span class="hljs-params">metaclass=SingletonMeta</span>):</span>
    <span class="hljs-string">"""
    Simple logging singleton using metaclass
    """</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.logs = []

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">log</span>(<span class="hljs-params">self, message</span>):</span>
        self.logs.append(message)
        print(<span class="hljs-string">f"[LOG] <span class="hljs-subst">{message}</span>"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_logs</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.logs
</code></pre>
<p>Let’s test the above metaclass approach to building a singleton:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Use the logger from different parts of code</span>
logger1 = Logger()
logger1.log(<span class="hljs-string">"Application started"</span>)
logger1.log(<span class="hljs-string">"User logged in"</span>)

<span class="hljs-comment"># Another part of code gets the same logger</span>
logger2 = Logger()
logger2.log(<span class="hljs-string">"Processing request"</span>)

print(<span class="hljs-string">f"\nTotal logs in logger1: <span class="hljs-subst">{len(logger1.get_logs())}</span>"</span>)
print(<span class="hljs-string">f"Total logs in logger2: <span class="hljs-subst">{len(logger2.get_logs())}</span>"</span>)
print(<span class="hljs-string">f"Same logger? <span class="hljs-subst">{logger1 <span class="hljs-keyword">is</span> logger2}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">[LOG] Application started
[LOG] User logged in
[LOG] Processing request

Total logs in logger1: 3
Total logs in logger2: 3
Same logger? True
</code></pre>
<p>The metaclass approach is elegant if you're comfortable with metaclasses. The <code>__call__</code> method intercepts class instantiation, allowing us to return the existing instance. This happens at a deeper level than <code>__new__</code>, making it more robust.</p>
<p>However, metaclasses add complexity. Most Python developers don't work with them regularly, making code harder to understand. Use this approach only if you need the additional control metaclasses provide.</p>
<h2 id="heading-thread-safe-singleton">Thread-Safe Singleton</h2>
<p>The previous implementations aren't thread-safe. In multi-threaded applications, two threads might create instances simultaneously. Let's fix that.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> threading

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThreadSafeSingleton</span>:</span>
    <span class="hljs-string">"""
    Thread-safe singleton using a lock
    """</span>
    _instance = <span class="hljs-literal">None</span>
    _lock = threading.Lock()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__new__</span>(<span class="hljs-params">cls</span>):</span>
        <span class="hljs-keyword">if</span> cls._instance <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
            <span class="hljs-keyword">with</span> cls._lock:
                <span class="hljs-comment"># Double-check pattern</span>
                <span class="hljs-keyword">if</span> cls._instance <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
                    print(<span class="hljs-string">f"Thread <span class="hljs-subst">{threading.current_thread().name}</span>: Creating instance"</span>)
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = <span class="hljs-literal">False</span>
        <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</span>):</span>
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self._initialized:
            <span class="hljs-keyword">with</span> self._lock:
                <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self._initialized:
                    print(<span class="hljs-string">f"Thread <span class="hljs-subst">{threading.current_thread().name}</span>: Initializing"</span>)
                    self.data = {}
                    self._initialized = <span class="hljs-literal">True</span>
</code></pre>
<p>Now let’s test the above singleton with multiple threads and verify that it’s a singleton with only one instance:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Test with multiple threads</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_singleton</span>(<span class="hljs-params">thread_id</span>):</span>
    instance = ThreadSafeSingleton()
    instance.data[thread_id] = <span class="hljs-string">f"Data from thread <span class="hljs-subst">{thread_id}</span>"</span>

threads = []
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">5</span>):
    t = threading.Thread(target=create_singleton, args=(i,), name=<span class="hljs-string">f"Thread-<span class="hljs-subst">{i}</span>"</span>)
    threads.append(t)
    t.start()

<span class="hljs-keyword">for</span> t <span class="hljs-keyword">in</span> threads:
    t.join()

<span class="hljs-comment"># Verify it's a singleton</span>
final = ThreadSafeSingleton()
print(<span class="hljs-string">f"\nShared data across all threads: <span class="hljs-subst">{final.data}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Thread Thread-0: Creating instance
Thread Thread-0: Initializing

Shared data across all threads: 
{0: 'Data from thread 0', 
1: 'Data from thread 1', 
2: 'Data from thread 2', 
3: 'Data from thread 3', 
4: 'Data from thread 4'}
</code></pre>
<p>The lock ensures that only one thread creates the instance. The double-check pattern avoids acquiring the lock on every access. We only lock when the instance might be None. This is more efficient than locking every time.</p>
<p><strong>Note on Python 3.13+</strong>: Python 3.13 introduced a <a target="_blank" href="https://docs.python.org/3/glossary.html#term-free-threading">build option for a free-threaded mode</a>, and this has become more <a target="_blank" href="https://docs.python.org/3/howto/free-threading-python.html">mainstream in Python 3.14</a>. With true parallelism, thread safety becomes even more essential. The <a target="_blank" href="https://docs.python.org/3/glossary.html#term-global-interpreter-lock">global interpreter lock (GIL)</a> previously masked some race conditions by preventing true parallel execution. In free-threaded Python, explicit locking like this becomes essential for correctness, not just good practice. If you're writing code for Python 3.13+ with free-threading enabled, always use proper synchronization primitives like locks for shared mutable state.</p>
<h2 id="heading-why-you-probably-shouldnt-use-singletons">Why You Probably Shouldn't Use Singletons</h2>
<p>Now that you know how to build singletons, let me explain why you often shouldn't.</p>
<p><strong>Singletons are global state in disguise.</strong> Global state makes code harder to understand, test, and maintain. When any code can access and modify a singleton, you lose the ability to reason about your code locally. Changes in one module can break another through shared state.</p>
<p><strong>Singletons make testing difficult.</strong> Tests should be independent, but Singletons carry state between tests. You need to reset the singleton before each test, which is error-prone. Worse, you can't easily mock a singleton for testing.</p>
<p><strong>Singletons violate the</strong> <a target="_blank" href="https://en.wikipedia.org/wiki/Single-responsibility_principle"><strong>Single Responsibility Principle</strong></a><strong>.</strong> The class handles both its core logic and the singleton mechanism. This mixing of concerns makes code harder to maintain.</p>
<p><strong>Python has better alternatives.</strong> Module-level objects are natural singletons. Dependency injection provides better control. Context managers handle resource lifetime cleanly.</p>
<h2 id="heading-better-alternatives-to-the-singleton-pattern">Better Alternatives to the Singleton Pattern</h2>
<p>Instead of singletons, consider these patterns.</p>
<h3 id="heading-module-level-instances"><strong>Module-level Instances</strong></h3>
<p><strong>Module-level instances</strong> are Python's natural singleton. Import a module, and you get the same instance every time. Here’s how you can do it:</p>
<pre><code class="lang-python"><span class="hljs-comment"># config.py</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Config</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.debug = <span class="hljs-literal">True</span>
        self.api_key = <span class="hljs-string">"secret-key"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update</span>(<span class="hljs-params">self, key, value</span>):</span>
        setattr(self, key, value)

<span class="hljs-comment"># Create a single instance at module level</span>
config = Config()
</code></pre>
<p>This is simpler and more Pythonic. The module system ensures you get the same instance. No special patterns needed. You can use it like so:</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>
<span class="hljs-keyword">from</span> config <span class="hljs-keyword">import</span> config

config.update(<span class="hljs-string">"debug"</span>, <span class="hljs-literal">False</span>)
print(<span class="hljs-string">f"Debug mode: <span class="hljs-subst">{config.debug}</span>"</span>)
</code></pre>
<p>Let’s now take a closer look at how and why this works. Python's module system is itself a singleton mechanism: when you import a module, Python executes it once and caches the result in <code>sys.modules</code>. Every subsequent import returns the cached module object, not a new one.</p>
<p>When <code>config.py</code> runs for the first time, it creates the <code>Config</code> instance and assigns it to the module-level variable <code>config</code>. This happens only once, during the initial import. Any other file that imports <code>config</code> from this module gets a reference to that same object, not a new instance. So <code>from config import config</code> in multiple files will always give you the exact same <code>Config</code> instance, achieving singleton behavior without any special patterns, metaclasses, or decorators.</p>
<h3 id="heading-dependency-injection"><strong>Dependency Injection</strong></h3>
<p><strong>Dependency injection</strong> gives you control without global state. It solves the singleton problem by making dependencies explicit parameters instead of hidden global state. Here’s an example:</p>
<pre><code class="lang-python"><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, connection_string</span>):</span>
        self.connection_string = connection_string

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">query</span>(<span class="hljs-params">self, sql</span>):</span>
        <span class="hljs-keyword">return</span> <span class="hljs-string">f"Executing <span class="hljs-subst">{sql}</span>"</span>

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

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_user</span>(<span class="hljs-params">self, user_id</span>):</span>
        <span class="hljs-keyword">return</span> self.db.query(<span class="hljs-string">f"SELECT * FROM users WHERE id = <span class="hljs-subst">{user_id}</span>"</span>)
</code></pre>
<p>Instead of <code>UserRepository</code> creating or accessing a global database singleton internally, it receives the database connection through its constructor (<code>__init__</code>). This means you control exactly which database instance gets used. In production you pass a real <code>DatabaseConnection</code>, but in tests you can pass a mock object that doesn't actually connect to a database.</p>
<p>The key here is that <code>UserRepository</code> doesn't know or care whether it's getting a singleton, a mock, or a fresh instance each time. It just knows it received something that has a <code>query</code> method.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create dependencies explicitly</span>
db = DatabaseConnection(<span class="hljs-string">"postgresql://localhost/mydb"</span>)
user_repo = UserRepository(db)

result = user_repo.get_user(<span class="hljs-number">123</span>)
</code></pre>
<p>This makes the code's dependencies visible in the function signature, eliminates hidden global state, makes testing trivial (just pass different objects), and gives you complete control over object lifecycles without needing any singleton patterns at all.</p>
<h2 id="heading-when-singletons-are-acceptable">When Singletons Are Acceptable</h2>
<p>Despite the drawbacks, some cases justify the use of singletons. Here are some of them:</p>
<p><strong>Hardware interfaces</strong> that represent unique physical resources. You might have one camera, one printer, or one GPIO interface. A singleton models this accurately.</p>
<p><strong>Caching layers</strong> where you want a single shared cache across your application. Though even here, dependency injection might be cleaner.</p>
<p><strong>Thread pools or connection pools</strong> where you want to limit and share expensive resources. The pool itself might be a singleton, though the resources it manages aren't.</p>
<p>Even in these cases, ask yourself: could I use dependency injection instead? Could I make this a module-level instance? The answer is often yes.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you found this tutorial helpful. You've learned several ways to implement singletons in Python: the classic pattern, decorators, metaclasses, and thread-safe variants. Each approach has trade-offs in complexity, readability, and thread safety.</p>
<p>More importantly, you've learned why singletons often aren't the best solution. Global state, testing difficulties, and violation of design principles make Singletons problematic. Module-level instances and dependency injection usually provide better alternatives.</p>
<p>When you reach for a Singleton, pause and ask: do I really need shared global state? Often the answer is no. But when you do need it, now you know how to implement it properly.</p>
<p>Use singletons sparingly, if at all.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work with the ORC File Format in Python – A Guide with Examples ]]>
                </title>
                <description>
                    <![CDATA[ If you've worked with big data or analytics platforms, you've probably heard about ORC files. But what exactly are they, and how can you work with them in Python? In this tutorial, I'll walk you through the basics of reading, writing, and manipulatin... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-the-orc-file-format-in-python-a-guide-with-examples/</link>
                <guid isPermaLink="false">6966f05c5adda8c9bd728d3b</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 14 Jan 2026 01:24:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768353870711/56ddb07c-56b0-4c3a-92ce-b1a86ea0e6e0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've worked with big data or analytics platforms, you've probably heard about <a target="_blank" href="https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC">ORC files</a>. But what exactly are they, and how can you work with them in Python?</p>
<p>In this tutorial, I'll walk you through the basics of reading, writing, and manipulating ORC files using Python. By the end, you'll understand when to use ORC and how to integrate it into your data pipelines.</p>
<p><a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/working-with-orc">You can find the code on GitHub</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-the-orc-file-format">What is the ORC File Format?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-reading-orc-files-in-python">Reading ORC Files in Python</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-writing-orc-files-with-compression">Writing ORC Files with Compression</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-working-with-complex-data-types">Working with Complex Data Types</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-a-more-helpful-example-processing-log-data">A More Helpful Example: Processing Log Data</a></p>
</li>
<li><p><a class="post-section-overview" href="#when-should-you-use-orc">When Should You Use ORC?</a></p>
</li>
</ol>
<h2 id="heading-what-is-the-orc-file-format">What Is the ORC File Format?</h2>
<p><a target="_blank" href="https://cwiki.apache.org/confluence/display/Hive/LanguageManual+ORC">ORC</a> stands for <strong>Optimized Row Columnar</strong>. It's a columnar storage file format designed for Hadoop workloads. Unlike traditional row-based formats like CSV, ORC stores data by columns, which makes it incredibly efficient for analytical queries.</p>
<p>Here's why ORC is popular:</p>
<ul>
<li><p>ORC files are highly compressed, often 75% smaller than text files</p>
</li>
<li><p>Columnar format means you only read the columns you need</p>
</li>
<li><p>You can add or remove columns without rewriting data</p>
</li>
<li><p>ORC includes lightweight indexes for faster queries</p>
</li>
</ul>
<p>Most organizations use ORC for their big data processing because it works well with <a target="_blank" href="https://hive.apache.org/">Apache Hive</a>, <a target="_blank" href="https://spark.apache.org/">Spark</a>, and <a target="_blank" href="https://prestodb.io/">Presto</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we get started, make sure you have:</p>
<ul>
<li><p>Python 3.10 or a later version installed</p>
</li>
<li><p>Basic understanding of DataFrames (pandas or similar)</p>
</li>
<li><p>Familiarity with file I/O operations</p>
</li>
</ul>
<p>You'll need to install these libraries:</p>
<pre><code class="lang-bash">pip install pyarrow pandas
</code></pre>
<p>So why do we need <a target="_blank" href="https://arrow.apache.org/docs/python/index.html">PyArrow</a>? PyArrow is the Python implementation of <a target="_blank" href="https://arrow.apache.org/">Apache Arrow</a>, which provides excellent support for columnar formats like ORC and Parquet. It's fast, memory-efficient, and actively maintained.</p>
<h2 id="heading-reading-orc-files-in-python">Reading ORC Files in Python</h2>
<p>Let's start by reading an ORC file. First, I'll show you how to create a sample ORC file so we have something to work with.</p>
<h3 id="heading-creating-a-sample-orc-file">Creating a Sample ORC File</h3>
<p>Here's how we'll create a simple employee dataset and save it as ORC:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">import</span> pyarrow <span class="hljs-keyword">as</span> pa
<span class="hljs-keyword">import</span> pyarrow.orc <span class="hljs-keyword">as</span> orc

<span class="hljs-comment"># Create sample employee data</span>
data = {
    <span class="hljs-string">'employee_id'</span>: [<span class="hljs-number">101</span>, <span class="hljs-number">102</span>, <span class="hljs-number">103</span>, <span class="hljs-number">104</span>, <span class="hljs-number">105</span>],
    <span class="hljs-string">'name'</span>: [<span class="hljs-string">'Alice Johnson'</span>, <span class="hljs-string">'Bob Smith'</span>, <span class="hljs-string">'Carol White'</span>, <span class="hljs-string">'David Brown'</span>, <span class="hljs-string">'Eve Davis'</span>],
    <span class="hljs-string">'department'</span>: [<span class="hljs-string">'Engineering'</span>, <span class="hljs-string">'Sales'</span>, <span class="hljs-string">'Engineering'</span>, <span class="hljs-string">'HR'</span>, <span class="hljs-string">'Sales'</span>],
    <span class="hljs-string">'salary'</span>: [<span class="hljs-number">95000</span>, <span class="hljs-number">65000</span>, <span class="hljs-number">88000</span>, <span class="hljs-number">72000</span>, <span class="hljs-number">71000</span>],
    <span class="hljs-string">'years_experience'</span>: [<span class="hljs-number">5</span>, <span class="hljs-number">3</span>, <span class="hljs-number">7</span>, <span class="hljs-number">4</span>, <span class="hljs-number">3</span>]
}

df = pd.DataFrame(data)

<span class="hljs-comment"># Convert to PyArrow Table and write as ORC</span>
table = pa.Table.from_pandas(df)
orc.write_table(table, <span class="hljs-string">'employees.orc'</span>)

print(<span class="hljs-string">"ORC file created successfully!"</span>)
</code></pre>
<p>This outputs:</p>
<pre><code class="lang-plaintext">ORC file created successfully!
</code></pre>
<p>Let me break down what's happening here. We start with a pandas DataFrame containing employee information. Then we convert it to a PyArrow table, which is PyArrow's in-memory representation of columnar data. Finally, we use <code>orc.write_table()</code> to write it to disk in ORC format.</p>
<p>The conversion to a PyArrow table is necessary because ORC is a columnar format, and PyArrow handles the translation from row-based pandas to column-based storage.</p>
<h3 id="heading-reading-the-orc-file">Reading the ORC File</h3>
<p>Now that we have an ORC file, let's read it back:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Read ORC file</span>
table = orc.read_table(<span class="hljs-string">'employees.orc'</span>)

<span class="hljs-comment"># Convert to pandas DataFrame for easier viewing</span>
df_read = table.to_pandas()

print(df_read)
print(<span class="hljs-string">f"\nData types:\n<span class="hljs-subst">{df_read.dtypes}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">   employee_id           name   department  salary  years_experience
0          101  Alice Johnson  Engineering   95000                 5
1          102      Bob Smith        Sales   65000                 3
2          103    Carol White  Engineering   88000                 7
3          104    David Brown           HR   72000                 4
4          105      Eve Davis        Sales   71000                 3

Data types:
employee_id          int64
name                object
department          object
salary               int64
years_experience     int64
dtype: object
</code></pre>
<p>The <code>orc.read_table()</code> function loads the entire ORC file into memory as a PyArrow table. We then convert it back to pandas for familiar DataFrame operations.</p>
<p>Notice how the data types are preserved. ORC maintains schema information, so your integers stay integers and strings stay strings.</p>
<h3 id="heading-reading-specific-columns">Reading Specific Columns</h3>
<p>Here's where ORC really shines. When working with large datasets, you often don't need all columns. ORC lets you read only what you need:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Read only specific columns</span>
table_subset = orc.read_table(<span class="hljs-string">'employees.orc'</span>, columns=[<span class="hljs-string">'name'</span>, <span class="hljs-string">'salary'</span>])
df_subset = table_subset.to_pandas()

print(df_subset)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">            name  salary
0  Alice Johnson   95000
1      Bob Smith   65000
2    Carol White   88000
3    David Brown   72000
4      Eve Davis   71000
</code></pre>
<p>This is called column pruning, and it's a massive performance optimization. If your ORC file has 50 columns but you only need 3, you're reading a fraction of the data. This translates to faster load times and lower memory usage.</p>
<h2 id="heading-writing-orc-files-with-compression">Writing ORC Files with Compression</h2>
<p>ORC supports multiple compression codecs. Let's explore how to use compression when writing files:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create a larger dataset</span>
large_data = {
    <span class="hljs-string">'id'</span>: range(<span class="hljs-number">10000</span>),
    <span class="hljs-string">'value'</span>: [<span class="hljs-string">f"data_<span class="hljs-subst">{i}</span>"</span> <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">10000</span>)],
    <span class="hljs-string">'category'</span>: [<span class="hljs-string">'A'</span>, <span class="hljs-string">'B'</span>, <span class="hljs-string">'C'</span>, <span class="hljs-string">'D'</span>] * <span class="hljs-number">2500</span>
}

df_large = pd.DataFrame(large_data)
table_large = pa.Table.from_pandas(df_large)

<span class="hljs-comment"># Write with ZLIB compression (default)</span>
orc.write_table(table_large, <span class="hljs-string">'data_zlib.orc'</span>, compression=<span class="hljs-string">'ZLIB'</span>)

<span class="hljs-comment"># Write with SNAPPY compression (faster but less compression)</span>
orc.write_table(table_large, <span class="hljs-string">'data_snappy.orc'</span>, compression=<span class="hljs-string">'SNAPPY'</span>)

<span class="hljs-comment"># Write with ZSTD compression (good balance)</span>
orc.write_table(table_large, <span class="hljs-string">'data_zstd.orc'</span>, compression=<span class="hljs-string">'ZSTD'</span>)

<span class="hljs-keyword">import</span> os
print(<span class="hljs-string">f"ZLIB size: <span class="hljs-subst">{os.path.getsize(<span class="hljs-string">'data_zlib.orc'</span>):,}</span> bytes"</span>)
print(<span class="hljs-string">f"SNAPPY size: <span class="hljs-subst">{os.path.getsize(<span class="hljs-string">'data_snappy.orc'</span>):,}</span> bytes"</span>)
print(<span class="hljs-string">f"ZSTD size: <span class="hljs-subst">{os.path.getsize(<span class="hljs-string">'data_zstd.orc'</span>):,}</span> bytes"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">ZLIB size: 23,342 bytes
SNAPPY size: 44,978 bytes
ZSTD size: 6,380 bytes
</code></pre>
<p>Different compression codecs offer different trade-offs. <a target="_blank" href="https://docs.python.org/3/library/zlib.html">ZLIB</a> gives better compression but is slower. <a target="_blank" href="https://github.com/google/snappy">SNAPPY</a> is faster but produces larger files. <a target="_blank" href="https://github.com/facebook/zstd">ZSTD</a> offers a good balance between compression ratio and speed.</p>
<p>For most use cases, I recommend ZSTD. It's fast enough for real-time processing and provides excellent compression.</p>
<h2 id="heading-working-with-complex-data-types">Working with Complex Data Types</h2>
<p>ORC handles nested data structures well. Here's how to work with lists and nested data:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Create data with complex types</span>
complex_data = {
    <span class="hljs-string">'user_id'</span>: [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>],
    <span class="hljs-string">'name'</span>: [<span class="hljs-string">'Alice'</span>, <span class="hljs-string">'Bob'</span>, <span class="hljs-string">'Carol'</span>],
    <span class="hljs-string">'purchases'</span>: [
        [<span class="hljs-string">'laptop'</span>, <span class="hljs-string">'mouse'</span>],
        [<span class="hljs-string">'keyboard'</span>],
        [<span class="hljs-string">'monitor'</span>, <span class="hljs-string">'cable'</span>, <span class="hljs-string">'stand'</span>]
    ],
    <span class="hljs-string">'ratings'</span>: [
        [<span class="hljs-number">4.5</span>, <span class="hljs-number">5.0</span>],
        [<span class="hljs-number">3.5</span>],
        [<span class="hljs-number">4.0</span>, <span class="hljs-number">4.5</span>, <span class="hljs-number">5.0</span>]
    ]
}

df_complex = pd.DataFrame(complex_data)
table_complex = pa.Table.from_pandas(df_complex)
orc.write_table(table_complex, <span class="hljs-string">'complex_data.orc'</span>)

<span class="hljs-comment"># Read it back</span>
table_read = orc.read_table(<span class="hljs-string">'complex_data.orc'</span>)
df_read = table_read.to_pandas()

print(df_read)
print(<span class="hljs-string">f"\nType of 'purchases' column: <span class="hljs-subst">{type(df_read[<span class="hljs-string">'purchases'</span>][<span class="hljs-number">0</span>])}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">   user_id   name                purchases          ratings
0        1  Alice          [laptop, mouse]       [4.5, 5.0]
1        2    Bob               [keyboard]            [3.5]
2        3  Carol  [monitor, cable, stand]  [4.0, 4.5, 5.0]

Type of 'purchases' column: &lt;class 'numpy.ndarray'&gt;
</code></pre>
<p>ORC preserves list structures, which is incredibly useful for storing JSON-like data or aggregated information. Each cell can contain a list, and ORC handles the variable-length storage efficiently.</p>
<h2 id="heading-a-more-helpful-example-processing-log-data">A More Helpful Example: Processing Log Data</h2>
<p>Let's put this together with a practical example. Imagine you're processing web server logs:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime, timedelta
<span class="hljs-keyword">import</span> random

<span class="hljs-comment"># Generate sample log data</span>
log_data = []
start_date = datetime(<span class="hljs-number">2025</span>, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>)

<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(<span class="hljs-number">1000</span>):
    log_data.append({
        <span class="hljs-string">'timestamp'</span>: start_date + timedelta(minutes=i),
        <span class="hljs-string">'user_id'</span>: random.randint(<span class="hljs-number">1000</span>, <span class="hljs-number">9999</span>),
        <span class="hljs-string">'endpoint'</span>: random.choice([<span class="hljs-string">'/api/users'</span>, <span class="hljs-string">'/api/products'</span>, <span class="hljs-string">'/api/orders'</span>]),
        <span class="hljs-string">'status_code'</span>: random.choice([<span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">200</span>, <span class="hljs-number">404</span>, <span class="hljs-number">500</span>]),
        <span class="hljs-string">'response_time_ms'</span>: random.randint(<span class="hljs-number">50</span>, <span class="hljs-number">2000</span>)
    })

df_logs = pd.DataFrame(log_data)

<span class="hljs-comment"># Write logs to ORC</span>
table_logs = pa.Table.from_pandas(df_logs)
orc.write_table(table_logs, <span class="hljs-string">'server_logs.orc'</span>, compression=<span class="hljs-string">'ZSTD'</span>)

<span class="hljs-comment"># Later, query only failed requests</span>
table_subset = orc.read_table(<span class="hljs-string">'server_logs.orc'</span>)
df_subset = table_subset.to_pandas()

<span class="hljs-comment"># Filter for errors</span>
errors = df_subset[df_subset[<span class="hljs-string">'status_code'</span>] &gt;= <span class="hljs-number">400</span>]
print(<span class="hljs-string">f"Total errors: <span class="hljs-subst">{len(errors)}</span>"</span>)
print(<span class="hljs-string">f"\nError breakdown:\n<span class="hljs-subst">{errors[<span class="hljs-string">'status_code'</span>].value_counts()}</span>"</span>)
print(<span class="hljs-string">f"\nSlowest error response: <span class="hljs-subst">{errors[<span class="hljs-string">'response_time_ms'</span>].max()}</span>ms"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Total errors: 387

Error breakdown:
status_code
404    211
500    176
Name: count, dtype: int64

Slowest error response: 1994ms
</code></pre>
<p>This example shows how ORC files are suitable file formats for log storage. You can write logs continuously, compress them efficiently, and query them quickly. The columnar format means you can filter by status code without reading endpoint or response time data.</p>
<h2 id="heading-when-should-you-use-orc">When Should You Use ORC?</h2>
<p>Use ORC when you:</p>
<ul>
<li><p>Work with big data platforms (Hadoop, Spark, Hive)</p>
</li>
<li><p>Need efficient storage for analytics workloads</p>
</li>
<li><p>Have wide tables where you often query specific columns</p>
</li>
<li><p>Want built-in compression and indexing</p>
</li>
</ul>
<p>Don't use ORC when you:</p>
<ul>
<li><p>Need row-by-row processing – use <a target="_blank" href="https://avro.apache.org/">Avro</a> instead</p>
</li>
<li><p>Work with small datasets – CSV is simpler in such cases</p>
</li>
<li><p>Need human-readable files – <a target="_blank" href="https://www.freecodecamp.org/news/how-to-parse-json-in-python-with-examples/">use JSON</a></p>
</li>
<li><p>Don't have big data infrastructure</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>ORC is a powerful format for data engineering and analytics. With PyArrow, working with ORC in Python is both straightforward and performant.</p>
<p>You've learned how to read and write ORC files, use compression, handle complex data types, and apply these concepts to real-world scenarios. The columnar storage and compression make ORC an excellent choice for big data pipelines.</p>
<p>Try integrating ORC into your next data project. You'll likely see significant improvements in storage costs and query performance.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Perform Secure Hashing Using Python's hashlib Module ]]>
                </title>
                <description>
                    <![CDATA[ Hashing is a fundamental technique in programming that converts data into a fixed-size string of characters. Unlike encryption, hashing is a one-way process: you can't reverse it to get the original data back. This makes hashing perfect for storing p... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-perform-secure-hashing-using-pythons-hashlib-module/</link>
                <guid isPermaLink="false">69409201ee1f8b8545e218b8</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Hashing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Security ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Mon, 15 Dec 2025 22:56:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765839274048/2110b9d7-00c4-4e85-a69b-7223f21f2ac3.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hashing is a fundamental technique in programming that converts data into a fixed-size string of characters. Unlike encryption, hashing is a one-way process: you can't reverse it to get the original data back.</p>
<p>This makes hashing perfect for storing passwords, verifying file integrity, and creating unique identifiers. In this tutorial, you'll learn how to use <a target="_blank" href="https://docs.python.org/3/library/hashlib.html">Python's built-in <code>hashlib</code> module</a> to implement secure hashing in your applications.</p>
<p>By the end of this tutorial, you'll understand:</p>
<ul>
<li><p>How to create basic hashes with different algorithms</p>
</li>
<li><p>Why simple hashing isn't enough for passwords</p>
</li>
<li><p>How to add salt to prevent rainbow table attacks</p>
</li>
<li><p>How to use key derivation functions for password storage</p>
</li>
</ul>
<p><a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/secure-hashing">You can find the code on GitHub</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow this tutorial, you should have:</p>
<ul>
<li><p><strong>Basic Python</strong>: Variables, data types, functions, and control structures</p>
</li>
<li><p><strong>Understanding of strings and bytes</strong>: How to encode strings and work with byte data</p>
</li>
</ul>
<p>No external libraries are required, as <a target="_blank" href="https://docs.python.org/3/library/hashlib.html">hashlib</a> and <a target="_blank" href="https://docs.python.org/3/library/os.html">os</a> are both part of Python's standard library.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-basic-hashing-with-pythons-hashlib">Basic Hashing with Python's hashlib</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-why-simple-hashing-isnt-enough-for-passwords">Why Simple Hashing Isn't Enough for Passwords</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-adding-salt-to-your-hashes">Adding Salt to Your Hashes</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-verifying-salted-passwords">Verifying Salted Passwords</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-using-key-derivation-functions">Using Key Derivation Functions</a></p>
</li>
</ol>
<h2 id="heading-basic-hashing-with-pythons-hashlib">Basic Hashing with Python’s hashlib</h2>
<p>Let's start with the fundamentals. The hashlib module provides access to several hashing algorithms like <a target="_blank" href="https://www.md5hashgenerator.com/">MD5</a>, <a target="_blank" href="https://en.wikipedia.org/wiki/SHA-1">SHA-1</a>, <a target="_blank" href="https://emn178.github.io/online-tools/sha256.html">SHA-256</a>, and more.</p>
<p>Here's how to create a simple SHA-256 hash:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> hashlib

<span class="hljs-comment"># Create a simple hash</span>
message = <span class="hljs-string">"Hello, World!"</span>
hash_object = hashlib.sha256(message.encode())
hex_digest = hash_object.hexdigest()

print(<span class="hljs-string">f"Original: <span class="hljs-subst">{message}</span>"</span>)
print(<span class="hljs-string">f"SHA-256 Hash: <span class="hljs-subst">{hex_digest}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Original: Hello, World!
SHA-256 Hash: dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f
</code></pre>
<p>Here, we import the hashlib module, encode our string to bytes using <code>.encode()</code> as hashlib requires bytes, not strings.</p>
<p>Then we create a hash object using <code>hashlib.sha256()</code> and get the hexadecimal representation with <code>.hexdigest()</code>.</p>
<p>The resulting hash is always 64 characters long regardless of input size. Meaning you have an output string that is <strong>256 bits long</strong>. As each hexadecimal character requires 4 bits, <strong>the output has 256/4 = 64 hexadecimal characters</strong>. Even changing one character produces a completely different hash.</p>
<p>Let's verify that:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> hashlib

<span class="hljs-comment"># Small change, big difference</span>
message1 = <span class="hljs-string">"Hello, World!"</span>
message2 = <span class="hljs-string">"Hello, World?"</span>  <span class="hljs-comment"># Only changed ! to ?</span>

hash1 = hashlib.sha256(message1.encode()).hexdigest()
hash2 = hashlib.sha256(message2.encode()).hexdigest()

print(<span class="hljs-string">f"Message 1: <span class="hljs-subst">{message1}</span>"</span>)
print(<span class="hljs-string">f"Hash 1:    <span class="hljs-subst">{hash1}</span>"</span>)
print(<span class="hljs-string">f"\nMessage 2: <span class="hljs-subst">{message2}</span>"</span>)
print(<span class="hljs-string">f"Hash 2:    <span class="hljs-subst">{hash2}</span>"</span>)
print(<span class="hljs-string">f"\nAre they the same? <span class="hljs-subst">{hash1 == hash2}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Message 1: Hello, World!
Hash 1:    dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f

Message 2: Hello, World?
Hash 2:    f16c3bb0532537acd5b2e418f2b1235b29181e35cffee7cc29d84de4a1d62e4d

Are they the same? False
</code></pre>
<p>This property is called the <a target="_blank" href="https://en.wikipedia.org/wiki/Avalanche_effect">avalanche effect</a> where a tiny change creates a completely different output.</p>
<h2 id="heading-why-simple-hashing-isnt-enough-for-passwords">Why Simple Hashing Isn't Enough for Passwords</h2>
<p>You might think you can just hash passwords and store them in your database. But there's a problem: attackers use <a target="_blank" href="https://en.wikipedia.org/wiki/Rainbow_table">rainbow tables</a>, which are precomputed databases of hashes for common passwords.</p>
<p>Here's what happens:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> hashlib

<span class="hljs-comment"># Simple password hashing (DON'T USE THIS!)</span>
password = <span class="hljs-string">"password123"</span>
hashed = hashlib.sha256(password.encode()).hexdigest()

print(<span class="hljs-string">f"Password: <span class="hljs-subst">{password}</span>"</span>)
print(<span class="hljs-string">f"Hash: <span class="hljs-subst">{hashed}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Password: password123
Hash: ef92b778bafe771e89245b89ecbc08a44a4e166c06659911881f383d4473e94f
</code></pre>
<p>If two users have the same password, they'll have identical hashes. An attacker who cracks one hash knows the password for all users with that hash.</p>
<p>So how do we handle this? Let’s learn in the next section.</p>
<h2 id="heading-adding-salt-to-your-hashes">Adding Salt to Your Hashes</h2>
<p>The solution is <strong>salting</strong>: adding random data to each password before hashing. This way, even identical passwords produce different hashes.</p>
<p>Here's how to implement salted hashing:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> hashlib
<span class="hljs-keyword">import</span> os

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hash_password_with_salt</span>(<span class="hljs-params">password</span>):</span>
    <span class="hljs-comment"># Generate a random salt (16 bytes = 128 bits)</span>
    salt = os.urandom(<span class="hljs-number">16</span>)

    <span class="hljs-comment"># Combine password and salt, then hash</span>
    hash_object = hashlib.sha256(salt + password.encode())
    password_hash = hash_object.hexdigest()

    <span class="hljs-comment"># Return both salt and hash (you need the salt to verify later)</span>
    <span class="hljs-keyword">return</span> salt.hex(), password_hash

<span class="hljs-comment"># Hash the same password twice</span>
password = <span class="hljs-string">"password123"</span>

salt1, hash1 = hash_password_with_salt(password)
salt2, hash2 = hash_password_with_salt(password)

print(<span class="hljs-string">f"Password: <span class="hljs-subst">{password}</span>\n"</span>)
print(<span class="hljs-string">f"First attempt:"</span>)
print(<span class="hljs-string">f"  Salt: <span class="hljs-subst">{salt1}</span>"</span>)
print(<span class="hljs-string">f"  Hash: <span class="hljs-subst">{hash1}</span>\n"</span>)
print(<span class="hljs-string">f"Second attempt:"</span>)
print(<span class="hljs-string">f"  Salt: <span class="hljs-subst">{salt2}</span>"</span>)
print(<span class="hljs-string">f"  Hash: <span class="hljs-subst">{hash2}</span>\n"</span>)
print(<span class="hljs-string">f"Same password, different hashes? <span class="hljs-subst">{hash1 != hash2}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Password: password123

First attempt:
  Salt: fc24b2d2245ff65b80c5bced38744171
  Hash: 5ce634c05941d25871e7ee334b5c24c75f64c4f6d557db66909fcaa793d869f9

Second attempt:
  Salt: bc8a1f79b07e56b51285557211f88bb0
  Hash: 043599d90b2aa0556265869cead35724c7d9d9d37129d897c6b68bade9e737e6

Same password, different hashes? True
</code></pre>
<p>How this works:</p>
<ul>
<li><p><code>os.urandom(16)</code> generates 16 random bytes, which is our salt</p>
</li>
<li><p>We concatenate the salt and password bytes before hashing</p>
</li>
<li><p>We return both the salt (as <code>hex</code>) and the hash</p>
</li>
<li><p>You must store both the salt and hash in your database</p>
</li>
</ul>
<p>When a user logs in, you retrieve their salt, hash the entered password with that salt, and compare the result to the stored hash.</p>
<h2 id="heading-verifying-salted-passwords">Verifying Salted Passwords</h2>
<p>Now let's create a function to verify passwords against salted hashes:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> hashlib
<span class="hljs-keyword">import</span> os

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hash_password</span>(<span class="hljs-params">password, salt=None</span>):</span>
    <span class="hljs-string">"""Hash a password with a salt. Generate new salt if not provided."""</span>
    <span class="hljs-keyword">if</span> salt <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
        salt = os.urandom(<span class="hljs-number">16</span>)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-comment"># Convert hex string back to bytes if needed</span>
        <span class="hljs-keyword">if</span> isinstance(salt, str):
            salt = bytes.fromhex(salt)

    password_hash = hashlib.sha256(salt + password.encode()).hexdigest()
    <span class="hljs-keyword">return</span> salt.hex(), password_hash

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">verify_password</span>(<span class="hljs-params">password, stored_salt, stored_hash</span>):</span>
    <span class="hljs-string">"""Verify a password against a stored salt and hash."""</span>
    <span class="hljs-comment"># Hash the provided password with the stored salt</span>
    _, new_hash = hash_password(password, stored_salt)

    <span class="hljs-comment"># Compare the hashes</span>
    <span class="hljs-keyword">return</span> new_hash == stored_hash
</code></pre>
<p>Here’s how you can use the above:</p>
<pre><code class="lang-python">print(<span class="hljs-string">"=== User Registration ==="</span>)
user_password = <span class="hljs-string">"mySecurePassword!"</span>
salt, password_hash = hash_password(user_password)
print(<span class="hljs-string">f"Password: <span class="hljs-subst">{user_password}</span>"</span>)
print(<span class="hljs-string">f"Salt: <span class="hljs-subst">{salt}</span>"</span>)
print(<span class="hljs-string">f"Hash: <span class="hljs-subst">{password_hash}</span>"</span>)

<span class="hljs-comment"># Simulate user login attempts</span>
print(<span class="hljs-string">"\n=== Login Attempts ==="</span>)
correct_attempt = <span class="hljs-string">"mySecurePassword!"</span>
wrong_attempt = <span class="hljs-string">"wrongPassword"</span>

print(<span class="hljs-string">f"Attempt 1: '<span class="hljs-subst">{correct_attempt}</span>'"</span>)
print(<span class="hljs-string">f"  Valid? <span class="hljs-subst">{verify_password(correct_attempt, salt, password_hash)}</span>"</span>)

print(<span class="hljs-string">f"\nAttempt 2: '<span class="hljs-subst">{wrong_attempt}</span>'"</span>)
print(<span class="hljs-string">f"  Valid? <span class="hljs-subst">{verify_password(wrong_attempt, salt, password_hash)}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">=== User Registration ===
Password: mySecurePassword!
Salt: 381779b5262deea84183e4b9454b98b1
Hash: 9756e1f0bc4c1aa4a72f35b0be8d3c8f430d31613371cf7de3c615bc475de98f

=== Login Attempts ===
Attempt 1: 'mySecurePassword!'
  Valid? True

Attempt 2: 'wrongPassword'
  Valid? False
</code></pre>
<p>This implementation shows a complete registration and login flow.</p>
<h2 id="heading-using-key-derivation-functions">Using Key Derivation Functions</h2>
<p>While salted SHA-256 is better than plain hashing, modern applications should use key derivation functions (KDFs) specifically designed for password hashing. These include <a target="_blank" href="https://www.npmjs.com/package/pbkdf2">PBKDF2</a> (Password-Based Key Derivation Function 2), <a target="_blank" href="https://bcrypt-generator.com/">bcrypt</a>, <a target="_blank" href="https://en.wikipedia.org/wiki/Scrypt">scrypt</a>, and <a target="_blank" href="https://en.wikipedia.org/wiki/Argon2">Argon2</a>. You can check the links to learn more about these key derivation functions.</p>
<p>These algorithms are intentionally slow and require more computational resources, making brute-force attacks much harder. Let's implement PBKDF2, which is built into Python:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> hashlib
<span class="hljs-keyword">import</span> os

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">hash_password_pbkdf2</span>(<span class="hljs-params">password, salt=None, iterations=<span class="hljs-number">600000</span></span>):</span>
    <span class="hljs-string">"""Hash password using PBKDF2 with SHA-256."""</span>
    <span class="hljs-keyword">if</span> salt <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
        salt = os.urandom(<span class="hljs-number">32</span>)  <span class="hljs-comment"># 32 bytes = 256 bits</span>
    <span class="hljs-keyword">elif</span> isinstance(salt, str):
        salt = bytes.fromhex(salt)

    <span class="hljs-comment"># PBKDF2 with 600,000 iterations (OWASP recommendation for 2024)</span>
    password_hash = hashlib.pbkdf2_hmac(
        <span class="hljs-string">'sha256'</span>,          <span class="hljs-comment"># Hash algorithm</span>
        password.encode(), <span class="hljs-comment"># Password as bytes</span>
        salt,              <span class="hljs-comment"># Salt as bytes</span>
        iterations,        <span class="hljs-comment"># Number of iterations</span>
        dklen=<span class="hljs-number">32</span>           <span class="hljs-comment"># Desired key length (32 bytes = 256 bits)</span>
    )

    <span class="hljs-keyword">return</span> salt.hex(), password_hash.hex(), iterations

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">verify_password_pbkdf2</span>(<span class="hljs-params">password, stored_salt, stored_hash, iterations</span>):</span>
    <span class="hljs-string">"""Verify password against PBKDF2 hash."""</span>
    _, new_hash, _ = hash_password_pbkdf2(password, stored_salt, iterations)
    <span class="hljs-keyword">return</span> new_hash == stored_hash

<span class="hljs-comment"># Hash a password</span>
print(<span class="hljs-string">"=== PBKDF2 Password Hashing ==="</span>)
password = <span class="hljs-string">"SuperSecure123!"</span>
salt, hash_value, iterations = hash_password_pbkdf2(password)

print(<span class="hljs-string">f"Password: <span class="hljs-subst">{password}</span>"</span>)
print(<span class="hljs-string">f"Salt: <span class="hljs-subst">{salt}</span>"</span>)
print(<span class="hljs-string">f"Hash: <span class="hljs-subst">{hash_value}</span>"</span>)
print(<span class="hljs-string">f"Iterations: <span class="hljs-subst">{iterations:,}</span>"</span>)
</code></pre>
<p>This outputs:</p>
<pre><code class="lang-plaintext">=== PBKDF2 Password Hashing ===
Password: SuperSecure123!
Salt: b388aecd774f6a7ddd95405091548bb50102c99beb1a10326a4c54070da4a3a5
Hash: c681450f41d0cec9ea2aad1108efe2a430b9c3d9fc3af621071be10ac9b3615a
Iterations: 600,000
</code></pre>
<p>Now let’s verify the password and also compare the speeds of SHA-256 vs. PBKDF2:</p>
<pre><code class="lang-python">print(<span class="hljs-string">"\n=== Verification ==="</span>)
is_valid = verify_password_pbkdf2(password, salt, hash_value, iterations)
print(<span class="hljs-string">f"Password valid? <span class="hljs-subst">{is_valid}</span>"</span>)

<span class="hljs-comment"># Show time comparison</span>
<span class="hljs-keyword">import</span> time

print(<span class="hljs-string">"\n=== Speed Comparison ==="</span>)
test_password = <span class="hljs-string">"test123"</span>

<span class="hljs-comment"># Simple SHA-256</span>
start = time.time()
<span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">100</span>):
    hashlib.sha256(test_password.encode()).hexdigest()
sha256_time = time.time() - start

<span class="hljs-comment"># PBKDF2</span>
start = time.time()
<span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> range(<span class="hljs-number">100</span>):
    hash_password_pbkdf2(test_password)
pbkdf2_time = time.time() - start

print(<span class="hljs-string">f"100 SHA-256 hashes: <span class="hljs-subst">{sha256_time:<span class="hljs-number">.3</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"100 PBKDF2 hashes: <span class="hljs-subst">{pbkdf2_time:<span class="hljs-number">.3</span>f}</span> seconds"</span>)
print(<span class="hljs-string">f"PBKDF2 is <span class="hljs-subst">{pbkdf2_time/sha256_time:<span class="hljs-number">.1</span>f}</span>x slower"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">
=== Verification ===
Password valid? True

=== Speed Comparison ===
100 SHA-256 hashes: 0.000 seconds
100 PBKDF2 hashes: 53.631 seconds
PBKDF2 is 240068.1x slower
</code></pre>
<p>How PBKDF2 works:</p>
<ul>
<li><p>Takes your password and salt</p>
</li>
<li><p>Applies the hash function (SHA-256) repeatedly – 600,000 times in this example</p>
</li>
<li><p>Each iteration makes the computation slower and harder to brute-force</p>
</li>
<li><p>You store the salt, hash, AND iteration count (so you can verify later)</p>
</li>
</ul>
<p>The iteration count can be increased over time as computers get faster. Modern recommendations (2024) suggest 600,000 iterations for PBKDF2-SHA256.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You've learned how to implement secure password hashing in Python using the <code>hashlib</code> module. Here are the key takeaways:</p>
<ul>
<li><p>Basic hashing with SHA-256 is useful for data integrity, not passwords</p>
</li>
<li><p>Salting prevents rainbow table attacks by making each hash unique</p>
</li>
<li><p>PBKDF2 adds computational cost through iterations, slowing down attackers</p>
</li>
<li><p>Always store the salt, hash, and iteration count together</p>
</li>
<li><p>Use key derivation functions (PBKDF2, bcrypt, Argon2) for passwords</p>
</li>
</ul>
<p>The code examples in this tutorial provide a solid foundation for implementing authentication in your projects. But remember, security is an ongoing process. Stay updated on best practices and regularly review your security implementations.</p>
<p>Happy (secure) coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work with YAML in Python – A Guide with Examples ]]>
                </title>
                <description>
                    <![CDATA[ If you've ever worked with configuration files, Docker Compose, Kubernetes, or CI/CD pipelines, you've probably used YAML. It's everywhere in modern development, and for good reason: it’s human-readable, simple, and powerful. In this guide, you'll le... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-yaml-in-python-a-guide-with-examples/</link>
                <guid isPermaLink="false">6939fb2773d2f31ad28cfef5</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ YAML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 10 Dec 2025 22:58:47 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765407508788/61769835-bd12-486e-8f8e-ba0f3a7af83c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've ever worked with configuration files, Docker Compose, Kubernetes, or CI/CD pipelines, you've probably used YAML. It's everywhere in modern development, and for good reason: it’s human-readable, simple, and powerful.</p>
<p>In this guide, you'll learn how to work with YAML files in Python. We'll cover reading, writing, and manipulating YAML data in practice.</p>
<p>🔗 <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/config-management-basics/working-with-yaml"><strong>You can find the code on GitHub</strong></a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before working with YAML in Python, you should have:</p>
<ul>
<li><p>Python 3.8 or a later version installed</p>
</li>
<li><p><strong>Basic Python knowledge</strong>: Variables, data types, functions, and control structures</p>
</li>
<li><p><strong>Understanding of data structures</strong>: Dictionaries, lists, and nested data structures</p>
</li>
<li><p><strong>File handling basics</strong>: Reading from and writing to files in Python</p>
</li>
<li><p><strong>Command line familiarity</strong>: Running Python scripts and installing packages with <code>pip</code></p>
</li>
</ul>
<p>You'll also need to install the <a target="_blank" href="https://pypi.org/project/PyYAML/">PyYAML</a> library:</p>
<pre><code class="lang-bash">pip install pyyaml
</code></pre>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-yaml-and-why-should-you-care">What Is YAML and Why Should You Care?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-yaml-files">How to Read YAML Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-yaml-files">How to Write YAML Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-work-with-lists-in-yaml">How to Work with Lists in YAML</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-build-a-yaml-config-manager">Build a YAML Config Manager</a></p>
</li>
</ol>
<h2 id="heading-what-is-yaml-and-why-should-you-care">What Is YAML and Why Should You Care?</h2>
<p>YAML (YAML Ain't Markup Language) is a data serialization format designed to be easy to read and write. Think of it as JSON's more readable cousin. :)</p>
<p>Here's the same data in JSON and YAML:</p>
<p>JSON:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"database"</span>: {
    <span class="hljs-attr">"host"</span>: <span class="hljs-string">"localhost"</span>,
    <span class="hljs-attr">"port"</span>: <span class="hljs-number">5432</span>,
    <span class="hljs-attr">"credentials"</span>: {
      <span class="hljs-attr">"username"</span>: <span class="hljs-string">"admin"</span>,
      <span class="hljs-attr">"password"</span>: <span class="hljs-string">"secret"</span>
    }
  }
}
</code></pre>
<p>YAML:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">database:</span>
  <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span>
  <span class="hljs-attr">port:</span> <span class="hljs-number">5432</span>
  <span class="hljs-attr">credentials:</span>
    <span class="hljs-attr">username:</span> <span class="hljs-string">admin</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">secret</span>
</code></pre>
<p>The YAML version is cleaner and easier to read, especially for configuration files.</p>
<h2 id="heading-how-to-read-yaml-files">How to Read YAML Files</h2>
<p>Let's say you have a configuration file for a web application. We'll create a simple <a target="_blank" href="https://github.com/balapriyac/python-basics/blob/main/config-management-basics/working-with-yaml/config.yaml"><code>config.yaml</code></a> file and learn how to read it in Python.</p>
<p>First, let's understand what we're trying to do. You have configuration data stored in a YAML file, and you want to load it into Python so you can use it in your application. Here’s how you can do it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml

<span class="hljs-comment"># Open and read the YAML file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'config.yaml'</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
    config = yaml.safe_load(file)

<span class="hljs-comment"># Access the data</span>
print(config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'host'</span>])
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">localhost
</code></pre>
<p>Here's what's happening in this code:</p>
<ul>
<li><p>We import the <code>yaml</code> module.</p>
</li>
<li><p>Then we open the file using a context manager (<code>with</code> statement), which automatically closes the file when we're done.</p>
</li>
<li><p>We use <code>yaml.safe_load()</code> to parse the YAML content into a Python dictionary so we can access the data just like any Python dictionary.</p>
</li>
</ul>
<p>⚠️ Note that you should <strong>always use</strong> <code>yaml.safe_load()</code> <strong>instead of</strong> <code>yaml.load()</code><strong>.</strong> The <code>safe_load()</code> function protects you from arbitrary code execution vulnerabilities. Unless you have a very specific reason (and you probably don't), stick with <code>safe_load()</code>.</p>
<h2 id="heading-how-to-write-yaml-files">How to Write YAML Files</h2>
<p>Now let's go in the opposite direction. You have Python data structures and you want to save them as YAML files. This is useful when you're generating configuration files or exporting data.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml

<span class="hljs-comment"># Your configuration data as Python dictionaries</span>
config = {
    <span class="hljs-string">'database'</span>: {
        <span class="hljs-string">'host'</span>: <span class="hljs-string">'localhost'</span>,
        <span class="hljs-string">'port'</span>: <span class="hljs-number">5432</span>,
        <span class="hljs-string">'name'</span>: <span class="hljs-string">'myapp_db'</span>,
        <span class="hljs-string">'credentials'</span>: {
            <span class="hljs-string">'username'</span>: <span class="hljs-string">'admin'</span>,
            <span class="hljs-string">'password'</span>: <span class="hljs-string">'secret123'</span>
        }
    },
    <span class="hljs-string">'server'</span>: {
        <span class="hljs-string">'host'</span>: <span class="hljs-string">'0.0.0.0'</span>,
        <span class="hljs-string">'port'</span>: <span class="hljs-number">8000</span>,
        <span class="hljs-string">'debug'</span>: <span class="hljs-literal">True</span>
    },
    <span class="hljs-string">'features'</span>: {
        <span class="hljs-string">'enable_cache'</span>: <span class="hljs-literal">True</span>,
        <span class="hljs-string">'cache_ttl'</span>: <span class="hljs-number">3600</span>
    }
}

<span class="hljs-comment"># Write to a YAML file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'generated_config.yaml'</span>, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> file:
    yaml.dump(config, file, default_flow_style=<span class="hljs-literal">False</span>)
</code></pre>
<p>Let's break down what's happening:</p>
<ul>
<li><p>We create a nested Python dictionary with our configuration.</p>
</li>
<li><p>We open a file in write mode (<code>'w'</code>).</p>
</li>
<li><p>We use <code>yaml.dump()</code> to convert the Python dictionary to YAML format and write it to the file.</p>
</li>
<li><p>The <code>default_flow_style=False</code> parameter ensures the output uses block style (the readable, indented format) instead of inline style.</p>
</li>
</ul>
<p>The resulting <code>generated_config.yaml</code> file will be properly formatted and ready to use.</p>
<h2 id="heading-how-to-work-with-lists-in-yaml">How to Work with Lists in YAML</h2>
<p>YAML handles lists elegantly, and they're common in configuration files. Suppose you're building a microservices application and need to configure multiple service endpoints. Here's how you'd work with that data:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml

<span class="hljs-comment"># Configuration with lists</span>
services_config = {
    <span class="hljs-string">'services'</span>: [
        {
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'auth-service'</span>,
            <span class="hljs-string">'url'</span>: <span class="hljs-string">'http://auth.example.com'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">30</span>
        },
        {
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'payment-service'</span>,
            <span class="hljs-string">'url'</span>: <span class="hljs-string">'http://payment.example.com'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">60</span>
        },
        {
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'notification-service'</span>,
            <span class="hljs-string">'url'</span>: <span class="hljs-string">'http://notification.example.com'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">15</span>
        }
    ],
    <span class="hljs-string">'retry_policy'</span>: {
        <span class="hljs-string">'max_attempts'</span>: <span class="hljs-number">3</span>,
        <span class="hljs-string">'backoff_seconds'</span>: <span class="hljs-number">5</span>
    }
}

<span class="hljs-comment"># Write to file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'services.yaml'</span>, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> file:
    yaml.dump(services_config, file, default_flow_style=<span class="hljs-literal">False</span>, sort_keys=<span class="hljs-literal">False</span>)

<span class="hljs-comment"># Read it back</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'services.yaml'</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
    loaded_services = yaml.safe_load(file)

<span class="hljs-comment"># Access list items</span>
<span class="hljs-keyword">for</span> service <span class="hljs-keyword">in</span> loaded_services[<span class="hljs-string">'services'</span>]:
    print(<span class="hljs-string">f"Service: <span class="hljs-subst">{service[<span class="hljs-string">'name'</span>]}</span>, URL: <span class="hljs-subst">{service[<span class="hljs-string">'url'</span>]}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Service: auth-service, URL: http://auth.example.com
Service: payment-service, URL: http://payment.example.com
Service: notification-service, URL: http://notification.example.com
</code></pre>
<p>This code helps us understand a few key concepts.</p>
<p>We can nest lists and dictionaries freely in our Python data structures. The <code>sort_keys=False</code> parameter preserves the order of keys as we defined them. When we read the YAML back, we can iterate over lists just like any Python list. The data structures in Python match the structures in YAML.</p>
<h2 id="heading-build-a-yaml-config-manager">Build a YAML Config Manager</h2>
<p>Let's put everything together with a practical example. We'll build a simple configuration manager class that handles environment-specific configs (a common need in real projects):</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml
<span class="hljs-keyword">import</span> os

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConfigManager</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, config_dir=<span class="hljs-string">'configs'</span></span>):</span>
        self.config_dir = config_dir
        self.config = {}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_config</span>(<span class="hljs-params">self, environment=<span class="hljs-string">'development'</span></span>):</span>
        <span class="hljs-string">"""Load configuration for a specific environment"""</span>
        config_file = os.path.join(self.config_dir, <span class="hljs-string">f'<span class="hljs-subst">{environment}</span>.yaml'</span>)

        <span class="hljs-keyword">try</span>:
            <span class="hljs-keyword">with</span> open(config_file, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
                self.config = yaml.safe_load(file)
            print(<span class="hljs-string">f"✓ Loaded configuration for <span class="hljs-subst">{environment}</span>"</span>)
            <span class="hljs-keyword">return</span> self.config
        <span class="hljs-keyword">except</span> FileNotFoundError:
            print(<span class="hljs-string">f"✗ Configuration file not found: <span class="hljs-subst">{config_file}</span>"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
        <span class="hljs-keyword">except</span> yaml.YAMLError <span class="hljs-keyword">as</span> e:
            print(<span class="hljs-string">f"✗ Error parsing YAML: <span class="hljs-subst">{e}</span>"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span>(<span class="hljs-params">self, key_path, default=None</span>):</span>
        <span class="hljs-string">"""Get a configuration value using dot notation"""</span>
        keys = key_path.split(<span class="hljs-string">'.'</span>)
        value = self.config

        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> keys:
            <span class="hljs-keyword">if</span> isinstance(value, dict) <span class="hljs-keyword">and</span> key <span class="hljs-keyword">in</span> value:
                value = value[key]
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">return</span> default

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

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_config</span>(<span class="hljs-params">self, environment, config_data</span>):</span>
        <span class="hljs-string">"""Save configuration to a file"""</span>
        config_file = os.path.join(self.config_dir, <span class="hljs-string">f'<span class="hljs-subst">{environment}</span>.yaml'</span>)

        os.makedirs(self.config_dir, exist_ok=<span class="hljs-literal">True</span>)

        <span class="hljs-keyword">with</span> open(config_file, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> file:
            yaml.dump(config_data, file, default_flow_style=<span class="hljs-literal">False</span>)

        print(<span class="hljs-string">f"✓ Saved configuration for <span class="hljs-subst">{environment}</span>"</span>)
</code></pre>
<p>This <code>ConfigManager</code> class shows you how to build a practical utility:</p>
<ol>
<li><p><strong>Initialization</strong>: We set up a directory for config files.</p>
</li>
<li><p><strong>Loading</strong>: The <code>load_config()</code> method reads environment-specific YAML files with proper error handling.</p>
</li>
<li><p><strong>Accessing data</strong>: The <code>get()</code> method lets you access nested values using dot notation (like <code>'database.host'</code>).</p>
</li>
<li><p><strong>Saving</strong>: The <code>save_config()</code> method writes configuration data to YAML files.</p>
</li>
</ol>
<p>This is the kind of pattern you might actually use in projects. You can extend it further by adding validation, environment variable overrides, or configuration merging. Here’s how you can use the <code>ConfigManager</code> class we’ve coded:</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    <span class="hljs-comment"># Create config manager</span>
    config_mgr = ConfigManager()

    <span class="hljs-comment"># Create a sample development config</span>
    dev_config = {
        <span class="hljs-string">'database'</span>: {
            <span class="hljs-string">'host'</span>: <span class="hljs-string">'localhost'</span>,
            <span class="hljs-string">'port'</span>: <span class="hljs-number">5432</span>,
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'dev_db'</span>
        },
        <span class="hljs-string">'api'</span>: {
            <span class="hljs-string">'base_url'</span>: <span class="hljs-string">'http://localhost:8000'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">30</span>
        }
    }

    <span class="hljs-comment"># Save it</span>
    config_mgr.save_config(<span class="hljs-string">'development'</span>, dev_config)

    <span class="hljs-comment"># Load and use it</span>
    config_mgr.load_config(<span class="hljs-string">'development'</span>)
    print(<span class="hljs-string">f"Database host: <span class="hljs-subst">{config_mgr.get(<span class="hljs-string">'database.host'</span>)}</span>"</span>)
    print(<span class="hljs-string">f"API timeout: <span class="hljs-subst">{config_mgr.get(<span class="hljs-string">'api.timeout'</span>)}</span>"</span>)
</code></pre>
<p>Running the above code should give you the following output:</p>
<pre><code class="lang-plaintext">✓ Saved configuration for development
✓ Loaded configuration for development
Database host: localhost
API timeout: 30
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>YAML is a powerful tool in your developer toolkit. It comes in handy when you’re configuring applications, defining CI/CD pipelines, or working with infrastructure as code.</p>
<p>In this article, you learned how to work with YAML files in Python. You can read configuration files, write data to YAML format, handle lists and nested structures, and build practical utilities like the <code>ConfigManager</code> we coded.</p>
<p>Start small. Try replacing a JSON config file in one of your projects with YAML. You'll quickly appreciate how much more readable it is, and you'll be comfortable working with YAML across the tools and platforms that use it.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Parse XML in Python Without Using External Libraries ]]>
                </title>
                <description>
                    <![CDATA[ In software development, you’ll run into XML (Extensible Markup Language) when working with configuration files, API responses, data exports, and more. While there are powerful third-party libraries for parsing XML, Python's standard library already ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-parse-xml-in-python-without-using-external-libraries/</link>
                <guid isPermaLink="false">6914ee442b7373beff5b13cb</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 12 Nov 2025 20:29:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1762979370762/de792485-6d8a-42aa-adcc-66bd797c207c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In software development, you’ll run into XML (Extensible Markup Language) when working with configuration files, API responses, data exports, and more. While there are powerful third-party libraries for parsing XML, Python's standard library already includes everything you need.</p>
<p>In this tutorial, you'll learn how to parse XML using Python's built-in <a target="_blank" href="https://docs.python.org/3/library/xml.etree.elementtree.html"><code>xml.etree.ElementTree</code></a> module. No pip installs required.</p>
<p>🔗 <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/parse-xml"><strong>You can find the code on GitHub</strong></a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you should have:</p>
<ul>
<li><p>Python 3.7 or later installed on your system</p>
</li>
<li><p>Basic understanding of Python syntax and data structures</p>
</li>
<li><p>Familiarity with basic programming concepts like loops and conditionals</p>
</li>
<li><p>A text editor or IDE for writing Python code</p>
</li>
</ul>
<p>No external libraries are required as we'll use Python's built-in <code>xml.etree.ElementTree</code> module.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-how-to-read-an-xml-string">How to Read an XML String</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-an-xml-file">How to Read an XML File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-find-elements-in-an-xml-tree">How to Find Elements in an XML Tree</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-extract-text-and-attributes-from-xml">How to Extract Text and Attributes from XML</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-a-simple-xml-parser">How to Build a Simple XML Parser</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-missing-data">How to Handle Missing Data</a></p>
</li>
</ol>
<h2 id="heading-how-to-read-an-xml-string">How to Read an XML String</h2>
<p>Let's start simple. We'll parse XML directly from a string to understand the fundamental concepts.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> xml.etree.ElementTree <span class="hljs-keyword">as</span> ET

xml_string = <span class="hljs-string">"""
&lt;catalog&gt;
    &lt;product id="101"&gt;
        &lt;name&gt;Wireless Keyboard&lt;/name&gt;
        &lt;price currency="USD"&gt;29.99&lt;/price&gt;
    &lt;/product&gt;
&lt;/catalog&gt;
"""</span>

root = ET.fromstring(xml_string)
print(<span class="hljs-string">f"Root tag: <span class="hljs-subst">{root.tag}</span>"</span>)
print(<span class="hljs-string">f"Root attributes: <span class="hljs-subst">{root.attrib}</span>"</span>)
</code></pre>
<p>How this works:</p>
<ul>
<li><p>We import <code>xml.etree.ElementTree</code> and give it the alias <code>ET</code> (this is the convention)</p>
</li>
<li><p><code>ET.fromstring()</code> parses the XML string and returns the <code>root</code> element</p>
</li>
<li><p>Every element has a <code>.tag</code> property (the element name) and <code>.attrib</code> dictionary (its attributes)</p>
</li>
<li><p>The <code>root</code> object represents the <code>&lt;catalog&gt;</code> element in our XML</p>
</li>
</ul>
<p>For the above example, you’ll see the following output:</p>
<pre><code class="lang-plaintext">Root tag: catalog
Root attributes: {}
</code></pre>
<p>Here, the <code>root.attrib</code> is empty because the root element <code>&lt;catalog&gt;</code> in the provided <code>xml_string</code> does not have any attributes defined. Attributes are key-value pairs within the opening tag of an XML element, like <code>id="101"</code> or <code>currency="USD"</code> in the <code>&lt;product&gt;</code> and <code>&lt;price&gt;</code> elements. Since <code>&lt;catalog&gt;</code> only has a tag and no additional information within its opening tag, its attributes dictionary is empty.</p>
<h2 id="heading-how-to-read-an-xml-file">How to Read an XML File</h2>
<p>In real applications, you'll usually read XML from files. Say you have a <a target="_blank" href="https://github.com/balapriyac/python-basics/blob/main/parse-xml/products.xml">products.xml</a> file. Here's how you can read from the XML file:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Parse an XML file</span>
tree = ET.parse(<span class="hljs-string">'products.xml'</span>)
root = tree.getroot()

print(<span class="hljs-string">f"Root element: <span class="hljs-subst">{root.tag}</span>"</span>)
</code></pre>
<p>Before we proceed to run and check the output, let’s note the differences between reading XML strings vs files:</p>
<ul>
<li><p><code>ET.parse()</code> reads from a file and returns an <code>ElementTree</code> object</p>
</li>
<li><p>We call <code>.getroot()</code> to get the <code>root</code> element</p>
</li>
<li><p>Use <code>ET.parse()</code> for files, <code>ET.fromstring()</code> for strings</p>
</li>
</ul>
<p>Running the above code should give you:</p>
<pre><code class="lang-plaintext">Root element: catalog
</code></pre>
<h2 id="heading-how-to-find-elements-in-an-xml-tree">How to Find Elements in an XML Tree</h2>
<p><code>ElementTree</code> gives you three main ways to search for elements. Understanding when to use each is important.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> xml.etree.ElementTree <span class="hljs-keyword">as</span> ET

xml_data = <span class="hljs-string">"""
&lt;catalog&gt;
    &lt;product id="101"&gt;
        &lt;name&gt;Wireless Keyboard&lt;/name&gt;
        &lt;categories&gt;
            &lt;category&gt;Electronics&lt;/category&gt;
            &lt;category&gt;Accessories&lt;/category&gt;
        &lt;/categories&gt;
    &lt;/product&gt;
    &lt;product id="102"&gt;
        &lt;name&gt;USB Mouse&lt;/name&gt;
        &lt;categories&gt;
            &lt;category&gt;Electronics&lt;/category&gt;
        &lt;/categories&gt;
    &lt;/product&gt;
&lt;/catalog&gt;
"""</span>

root = ET.fromstring(xml_data)

<span class="hljs-comment"># Method 1: find() - returns the FIRST matching element</span>
first_product = root.find(<span class="hljs-string">'product'</span>)
print(<span class="hljs-string">f"First product ID: <span class="hljs-subst">{first_product.get(<span class="hljs-string">'id'</span>)}</span>"</span>)

<span class="hljs-comment"># Method 2: findall() - returns ALL direct children that match</span>
all_products = root.findall(<span class="hljs-string">'product'</span>)
print(<span class="hljs-string">f"Total products: <span class="hljs-subst">{len(all_products)}</span>"</span>)

<span class="hljs-comment"># Method 3: iter() - recursively finds ALL matching elements</span>
all_categories = root.iter(<span class="hljs-string">'category'</span>)
category_list = [cat.text <span class="hljs-keyword">for</span> cat <span class="hljs-keyword">in</span> all_categories]
print(<span class="hljs-string">f"All categories: <span class="hljs-subst">{category_list}</span>"</span>)
</code></pre>
<p>Now let’s understand how the three methods work:</p>
<ul>
<li><p><code>find()</code> stops at the first match. Use when you only need one element.</p>
</li>
<li><p><code>findall()</code> only searches direct children (one level deep). Use for immediate child elements.</p>
</li>
<li><p><code>iter()</code> searches recursively through the entire tree. Use when elements might be nested anywhere.</p>
</li>
</ul>
<p>This is important: <code>findall('category')</code> on root won't find anything because <code>&lt;category&gt;</code> isn't a direct child of <code>&lt;catalog&gt;</code>. But <code>iter('category')</code> will find all categories no matter how deeply nested. So when you run the above code, you’ll get:</p>
<pre><code class="lang-plaintext">First product ID: 101
Total products: 2
All categories: ['Electronics', 'Accessories', 'Electronics']
</code></pre>
<h2 id="heading-how-to-extract-text-and-attributes-from-xml">How to Extract Text and Attributes from XML</h2>
<p>Now let's extract actual data from our XML. This is where you turn structured XML into Python data you can work with.</p>
<pre><code class="lang-python">xml_data = <span class="hljs-string">"""
&lt;catalog&gt;
    &lt;product id="101"&gt;
        &lt;name&gt;Wireless Keyboard&lt;/name&gt;
        &lt;price currency="USD"&gt;29.99&lt;/price&gt;
        &lt;stock&gt;45&lt;/stock&gt;
    &lt;/product&gt;
&lt;/catalog&gt;
"""</span>

root = ET.fromstring(xml_data)
product = root.find(<span class="hljs-string">'product'</span>)

<span class="hljs-comment"># Get element text content</span>
product_name = product.find(<span class="hljs-string">'name'</span>).text
price_text = product.find(<span class="hljs-string">'price'</span>).text
stock_text = product.find(<span class="hljs-string">'stock'</span>).text

<span class="hljs-comment"># Get attributes (two ways)</span>
product_id = product.get(<span class="hljs-string">'id'</span>)  <span class="hljs-comment"># Method 1: .get()</span>
product_id_alt = product.attrib[<span class="hljs-string">'id'</span>]  <span class="hljs-comment"># Method 2: .attrib dictionary</span>

<span class="hljs-comment"># Get nested attributes</span>
price_element = product.find(<span class="hljs-string">'price'</span>)
currency = price_element.get(<span class="hljs-string">'currency'</span>)

print(<span class="hljs-string">f"Product: <span class="hljs-subst">{product_name}</span>"</span>)
print(<span class="hljs-string">f"ID: <span class="hljs-subst">{product_id}</span>"</span>)
print(<span class="hljs-string">f"Price: <span class="hljs-subst">{currency}</span> <span class="hljs-subst">{price_text}</span>"</span>)
print(<span class="hljs-string">f"Stock: <span class="hljs-subst">{stock_text}</span>"</span>)
</code></pre>
<p>This outputs:</p>
<pre><code class="lang-plaintext">Product: Wireless Keyboard
ID: 101
Price: USD 29.99
Stock: 45
</code></pre>
<p>What's happening here:</p>
<ul>
<li><p><code>.text</code> gets the text content between opening and closing tags</p>
</li>
<li><p><code>.get('attribute_name')</code> safely retrieves an attribute (returns <code>None</code> if missing)</p>
</li>
<li><p><code>.attrib['attribute_name']</code> accesses the attribute dictionary directly (raises <code>KeyError</code> if missing)</p>
</li>
<li><p>Use <code>.get()</code> when an attribute might be optional, use <code>.attrib[]</code> when it's required</p>
</li>
</ul>
<h2 id="heading-how-to-build-a-simple-xml-parser">How to Build a Simple XML Parser</h2>
<p>Let's put it all together with a practical example. We'll parse the full product catalog and convert it to a Python list of dictionaries.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">parse_product_catalog</span>(<span class="hljs-params">xml_file</span>):</span>
    <span class="hljs-string">"""Parse an XML product catalog and return a list of product dictionaries."""</span>
    tree = ET.parse(xml_file)
    root = tree.getroot()

    products = []

    <span class="hljs-keyword">for</span> product_element <span class="hljs-keyword">in</span> root.findall(<span class="hljs-string">'product'</span>):
        <span class="hljs-comment"># Extract product data</span>
        product = {
            <span class="hljs-string">'id'</span>: product_element.get(<span class="hljs-string">'id'</span>),
            <span class="hljs-string">'name'</span>: product_element.find(<span class="hljs-string">'name'</span>).text,
            <span class="hljs-string">'price'</span>: float(product_element.find(<span class="hljs-string">'price'</span>).text),
            <span class="hljs-string">'currency'</span>: product_element.find(<span class="hljs-string">'price'</span>).get(<span class="hljs-string">'currency'</span>),
            <span class="hljs-string">'stock'</span>: int(product_element.find(<span class="hljs-string">'stock'</span>).text),
            <span class="hljs-string">'categories'</span>: []
        }

        <span class="hljs-comment"># Extract categories (nested elements)</span>
        categories_element = product_element.find(<span class="hljs-string">'categories'</span>)
        <span class="hljs-keyword">if</span> categories_element <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
            <span class="hljs-keyword">for</span> category <span class="hljs-keyword">in</span> categories_element.findall(<span class="hljs-string">'category'</span>):
                product[<span class="hljs-string">'categories'</span>].append(category.text)

        products.append(product)

    <span class="hljs-keyword">return</span> products
</code></pre>
<p>Breaking down this parser:</p>
<ul>
<li><p>We iterate through all <code>&lt;product&gt;</code> elements using <code>findall()</code></p>
</li>
<li><p>For each product, we extract text and attributes into a dictionary. We convert numeric strings to proper types (<code>float</code> for price, <code>int</code> for stock)</p>
</li>
<li><p>For nested categories, we first check if the <code>&lt;categories&gt;</code> element exists. Then we iterate through child <code>&lt;category&gt;</code> elements and collect their text</p>
</li>
</ul>
<p>The result is clean Python data structures you can easily work with. You can now use the parser like so:</p>
<pre><code class="lang-python">products = parse_product_catalog(<span class="hljs-string">'products.xml'</span>)

<span class="hljs-keyword">for</span> product <span class="hljs-keyword">in</span> products:
    print(<span class="hljs-string">f"\nProduct: <span class="hljs-subst">{product[<span class="hljs-string">'name'</span>]}</span>"</span>)
    print(<span class="hljs-string">f"  ID: <span class="hljs-subst">{product[<span class="hljs-string">'id'</span>]}</span>"</span>)
    print(<span class="hljs-string">f"  Price: <span class="hljs-subst">{product[<span class="hljs-string">'currency'</span>]}</span> <span class="hljs-subst">{product[<span class="hljs-string">'price'</span>]}</span>"</span>)
    print(<span class="hljs-string">f"  Stock: <span class="hljs-subst">{product[<span class="hljs-string">'stock'</span>]}</span>"</span>)
    print(<span class="hljs-string">f"  Categories: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(product[<span class="hljs-string">'categories'</span>])}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Product: Wireless Keyboard
  ID: 101
  Price: USD 29.99
  Stock: 45
  Categories: Electronics, Accessories

Product: USB Mouse
  ID: 102
  Price: USD 15.99
  Stock: 120
  Categories: Electronics
</code></pre>
<h2 id="heading-how-to-handle-missing-data">How to Handle Missing Data</h2>
<p>Real-world XML is messy (no surprises there!). Elements might be missing, text might be empty, or attributes might not exist. Here's how to handle that gracefully.</p>
<pre><code class="lang-python">xml_data = <span class="hljs-string">"""
&lt;catalog&gt;
    &lt;product id="101"&gt;
        &lt;name&gt;Wireless Keyboard&lt;/name&gt;
        &lt;price currency="USD"&gt;29.99&lt;/price&gt;
    &lt;/product&gt;
    &lt;product id="102"&gt;
        &lt;name&gt;USB Mouse&lt;/name&gt;
        &lt;!-- Missing price element --&gt;
    &lt;/product&gt;
&lt;/catalog&gt;
"""</span>

root = ET.fromstring(xml_data)

<span class="hljs-keyword">for</span> product <span class="hljs-keyword">in</span> root.findall(<span class="hljs-string">'product'</span>):
    name = product.find(<span class="hljs-string">'name'</span>).text

    <span class="hljs-comment"># Safe way to handle potentially missing elements</span>
    price_element = product.find(<span class="hljs-string">'price'</span>)
    <span class="hljs-keyword">if</span> price_element <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">None</span>:
        price = float(price_element.text)
        currency = price_element.get(<span class="hljs-string">'currency'</span>, <span class="hljs-string">'USD'</span>)  <span class="hljs-comment"># Default value</span>
        print(<span class="hljs-string">f"<span class="hljs-subst">{name}</span>: <span class="hljs-subst">{currency}</span> <span class="hljs-subst">{price}</span>"</span>)
    <span class="hljs-keyword">else</span>:
        print(<span class="hljs-string">f"<span class="hljs-subst">{name}</span>: Price not available"</span>)
</code></pre>
<p>Here, we handle potential missing data by:</p>
<ol>
<li><p>Using <code>product.find('price')</code> to search for the <code>&lt;price&gt;</code> element within the current <code>&lt;product&gt;</code> element.</p>
</li>
<li><p>Checking if the result of <code>find()</code> is <code>None</code>. If an element is not found, <code>find()</code> returns <code>None</code>.</p>
</li>
<li><p>Using an <code>if price_element is not None:</code> condition to only attempt to access the text <code>(price_element.text)</code> and attributes <code>(price_element.get('currency', 'USD'))</code> of the <code>&lt;price&gt;</code> element if it was actually found.</p>
</li>
<li><p>Adding an <code>else</code> block to handle the case where the <code>&lt;price&gt;</code> element is missing, printing "Price not available".</p>
</li>
</ol>
<p>This approach prevents errors that would occur if you tried to access <code>.text</code> or <code>.get()</code> on a <code>None</code> object. For the above code snippet, you’ll get:</p>
<pre><code class="lang-plaintext">Wireless Keyboard: USD 29.99
USB Mouse: Price not available
</code></pre>
<p>Here are a few more error-handling strategies:</p>
<ul>
<li><p>Always check if <code>find()</code> returns <code>None</code> before accessing <code>.text</code> or <code>.get()</code></p>
</li>
<li><p>Use <code>.get('attr', 'default')</code> to provide default values for missing attributes</p>
</li>
<li><p>Consider wrapping parsing in try-except blocks for production code</p>
</li>
<li><p>Validate your data after parsing rather than assuming XML structure is correct</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You now know how to parse XML in Python without installing any external libraries. You learned:</p>
<ul>
<li><p>How to read XML from strings and files</p>
</li>
<li><p>The difference between <code>find()</code>, <code>findall()</code>, and <code>iter()</code></p>
</li>
<li><p>How to extract text content and attributes safely</p>
</li>
<li><p>How to handle nested elements and missing data</p>
</li>
</ul>
<p>The <code>xml.etree.ElementTree</code> module works well enough for most XML parsing needs, and it's always available in Python's standard library.</p>
<p>For more advanced XML navigation and selection, you can explore <a target="_blank" href="https://www.w3schools.com/xml/xpath_syntax.asp">XPath expressions</a>. XPath works well for selecting nodes in an XML document and can be very useful for complex structures. We’ll cover this in another tutorial.</p>
<p>Until then, happy parsing!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Parse JSON in Python – A Complete Guide With Examples ]]>
                </title>
                <description>
                    <![CDATA[ JSON has become the standard format for data exchange on the web. So you'll run into JSON all the time when working with REST APIs, configuration files, database exports, and more. As a developer, you should know how to parse, manipulate, and generat... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-parse-json-in-python-with-examples/</link>
                <guid isPermaLink="false">69028d2168ede4f821f8f0e8</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ json ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 29 Oct 2025 21:54:41 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761774871223/d4b07c0a-d37e-4197-bb47-f1ee6f51dffd.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>JSON has become the standard format for data exchange on the web. So you'll run into JSON all the time when working with REST APIs, configuration files, database exports, and more. As a developer, you should know how to parse, manipulate, and generate JSON efficiently.</p>
<p>Python's <a target="_blank" href="https://docs.python.org/3/library/json.html">built-in json module</a> provides a straightforward interface for working with JSON data. You'll use it to convert JSON strings into Python dictionaries and lists that you can manipulate with familiar syntax, and then convert your Python data structures back into JSON when you need to send data to an API or save it to a file.</p>
<p>Beyond basic parsing, you'll often need to handle nested structures, validate data integrity, manage, and transform data formats. This guide covers practical JSON parsing techniques you can use in your projects right away. Let’s get started!</p>
<p>🔗 <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/parsing-json"><strong>You can find the code examples on GitHub</strong></a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you should have:</p>
<ul>
<li><p>Python 3.7 or later installed on your system</p>
</li>
<li><p>Basic understanding of Python dictionaries and lists</p>
</li>
<li><p>Familiarity with Python file operations (opening and reading files)</p>
</li>
<li><p>A text editor or IDE for writing Python code</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-understanding-json-structure-and-basic-parsing">Understanding JSON Structure and Basic Parsing</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-work-with-nested-json-objects">How to Work with Nested JSON Objects</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-parse-json-arrays">How to Parse JSON Arrays</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-json-from-files">How to Read JSON from Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-json-parsing-errors">How to Handle JSON Parsing Errors</a></p>
</li>
</ol>
<h2 id="heading-understanding-json-structure-and-basic-parsing">Understanding JSON Structure and Basic Parsing</h2>
<p>JSON represents data using a simple syntax with six data types: <strong>objects (key-value pairs), arrays, strings, numbers, Booleans,</strong> and <strong>null</strong>.</p>
<p>When Python parses JSON, these types map directly to Python equivalents:</p>
<ul>
<li><p>JSON objects become dictionaries,</p>
</li>
<li><p>arrays become lists,</p>
</li>
<li><p>strings remain strings,</p>
</li>
<li><p>numbers become <code>int</code> or <code>float</code>,</p>
</li>
<li><p>true and false become <code>True</code> and <code>False</code>, and</p>
</li>
<li><p>null becomes <code>None</code>.</p>
</li>
</ul>
<p>This direct mapping makes working with JSON in Python intuitive once you understand the correspondence.</p>
<p>Before you start, import the <code>json</code> module that’s built into the Python standard library.</p>
<p>The basic operation in JSON parsing is converting a JSON string into a Python data structure you can work with. Here's how to perform this basic conversion:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

json_string = <span class="hljs-string">'{"name": "Sarah Chen", "age": 28, "city": "Portland"}'</span>
person = json.loads(json_string)

print(person[<span class="hljs-string">"name"</span>]) 
print(person[<span class="hljs-string">"age"</span>])   
print(type(person))
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Sarah Chen
28
&lt;class 'dict'&gt;
</code></pre>
<p>Here, the <code>json.loads()</code> function takes a string containing JSON and returns a Python object. The <code>'s'</code> in <code>'loads'</code> stands for 'string', indicating it works with string data. After parsing, you have a regular Python dictionary that you can access with bracket notation using the JSON keys.</p>
<h2 id="heading-how-to-work-with-nested-json-objects">How to Work with Nested JSON Objects</h2>
<p>Real-world JSON data rarely comes in flat structures. APIs typically return deeply nested objects containing multiple levels of data. Understanding how to navigate these structures is essential for extracting the information you need.</p>
<p>Consider this example of parsing a weather API response that contains nested objects for location data and current conditions:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

weather_data = <span class="hljs-string">'''
{
    "location": {
        "city": "Seattle",
        "state": "WA",
        "coordinates": {
            "latitude": 47.6062,
            "longitude": -122.3321
        }
    },
    "current": {
        "temperature_f": 58,
        "conditions": "Partly Cloudy",
        "humidity": 72,
        "wind": {
            "speed_mph": 8,
            "direction": "NW"
        }
    }
}
'''</span>

weather = json.loads(weather_data)
</code></pre>
<p>After parsing the JSON string with <code>json.loads()</code>, you can access nested values by chaining dictionary keys together:</p>
<pre><code class="lang-python">city = weather[<span class="hljs-string">"location"</span>][<span class="hljs-string">"city"</span>]
temp = weather[<span class="hljs-string">"current"</span>][<span class="hljs-string">"temperature_f"</span>]
wind_speed = weather[<span class="hljs-string">"current"</span>][<span class="hljs-string">"wind"</span>][<span class="hljs-string">"speed_mph"</span>]

print(<span class="hljs-string">f"<span class="hljs-subst">{city}</span>: <span class="hljs-subst">{temp}</span>°F, Wind <span class="hljs-subst">{wind_speed}</span> mph"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Seattle: 58°F, Wind 8 mph
</code></pre>
<p>In this example, each level of nesting requires another set of brackets. The expression <code>weather["location"]["city"]</code> first accesses the <code>"location"</code> object, then retrieves the <code>"city"</code> value from within it. You can drill down as many levels as needed, like <code>weather["current"]["wind"]["speed_mph"]</code> which traverses three levels deep. This chaining syntax mirrors how you would access the data in the original JSON structure.</p>
<h2 id="heading-how-to-parse-json-arrays">How to Parse JSON Arrays</h2>
<p>JSON arrays represent ordered lists of values and appear frequently in API responses when returning collections of items. Python converts JSON arrays into lists, which you can iterate through or access by index.</p>
<p>Here's an example parsing a list of products from an inventory system:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

products_json = <span class="hljs-string">'''
[
    {
        "id": "PROD-001",
        "name": "Wireless Mouse",
        "price": 24.99,
        "in_stock": true
    },
    {
        "id": "PROD-002",
        "name": "Mechanical Keyboard",
        "price": 89.99,
        "in_stock": false
    },
    {
        "id": "PROD-003",
        "name": "USB-C Hub",
        "price": 34.99,
        "in_stock": true
    }
]
'''</span>

products = json.loads(products_json)
</code></pre>
<p>The JSON string starts with a square bracket, indicating an array at the root level. After parsing, products is a Python list containing three dictionaries.</p>
<p>You can now use standard Python list operations on the parsed data. The <code>len()</code> function returns the number of items, and you can iterate through the list with a for loop. Each iteration gives you a dictionary representing one product, which you access using dictionary syntax.</p>
<pre><code class="lang-python">print(<span class="hljs-string">f"Total products: <span class="hljs-subst">{len(products)}</span>"</span>)

<span class="hljs-keyword">for</span> product <span class="hljs-keyword">in</span> products:
    status = <span class="hljs-string">"Available"</span> <span class="hljs-keyword">if</span> product[<span class="hljs-string">"in_stock"</span>] <span class="hljs-keyword">else</span> <span class="hljs-string">"Out of stock"</span>
    print(<span class="hljs-string">f"<span class="hljs-subst">{product[<span class="hljs-string">'name'</span>]}</span>: $<span class="hljs-subst">{product[<span class="hljs-string">'price'</span>]}</span> - <span class="hljs-subst">{status}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Total products: 3
Wireless Mouse: $24.99 - Available
Mechanical Keyboard: $89.99 - Out of stock
USB-C Hub: $34.99 - Available
</code></pre>
<p>You can also access specific array elements by index and filter the data. List indexing works exactly as it does with any Python list, starting at zero.</p>
<pre><code class="lang-python">first_product = products[<span class="hljs-number">0</span>]
print(<span class="hljs-string">f"First product ID: <span class="hljs-subst">{first_product[<span class="hljs-string">'id'</span>]}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">First product ID: PROD-001
</code></pre>
<p>You can also use list comprehensions to filter the parsed data, creating a new list containing only products where the "in_stock" value is <code>True</code>.</p>
<pre><code class="lang-python">available_products = [p <span class="hljs-keyword">for</span> p <span class="hljs-keyword">in</span> products <span class="hljs-keyword">if</span> p[<span class="hljs-string">"in_stock"</span>]]
print(<span class="hljs-string">f"Available: <span class="hljs-subst">{len(available_products)}</span> products"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Available: 2 products
</code></pre>
<h2 id="heading-how-to-read-json-from-files">How to Read JSON from Files</h2>
<p>Most applications read JSON from files rather than hardcoded strings. Configuration files, data exports, and cached API responses typically live in JSON files that your application needs to load at runtime.</p>
<p>The <code>json</code> module comes with the <code>load</code> function for reading files that handles opening and parsing in one step.</p>
<p>This code creates a sample configuration file to demonstrate file reading:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

<span class="hljs-comment"># First, let's create a sample config </span>
config_data = {
    <span class="hljs-string">"api_url"</span>: <span class="hljs-string">"https://api.example.com/v2"</span>,
    <span class="hljs-string">"timeout"</span>: <span class="hljs-number">30</span>,
    <span class="hljs-string">"retry_attempts"</span>: <span class="hljs-number">3</span>,
    <span class="hljs-string">"enable_logging"</span>: <span class="hljs-literal">True</span>
}

<span class="hljs-keyword">with</span> open(<span class="hljs-string">'config.json'</span>, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> f:
    json.dump(config_data, f, indent=<span class="hljs-number">2</span>)
</code></pre>
<p>The <code>json.dump()</code> function writes Python data to a file, and the <code>indent=2</code> parameter formats the JSON with 2-space indentation to make it human-readable. The <code>'w'</code> mode opens the file for writing, creating it if it doesn't exist or overwriting it if it does.</p>
<p>Now you can read that file back into your application. The <code>json.load()</code> function (without the <code>'s'</code>) reads from a file object and parses the JSON in one operation.</p>
<pre><code class="lang-python"><span class="hljs-keyword">with</span> open(<span class="hljs-string">'config.json'</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> f:
    config = json.load(f)

print(<span class="hljs-string">f"API URL: <span class="hljs-subst">{config[<span class="hljs-string">'api_url'</span>]}</span>"</span>)
print(<span class="hljs-string">f"Timeout: <span class="hljs-subst">{config[<span class="hljs-string">'timeout'</span>]}</span> seconds"</span>)
print(<span class="hljs-string">f"Logging: <span class="hljs-subst">{<span class="hljs-string">'Enabled'</span> <span class="hljs-keyword">if</span> config[<span class="hljs-string">'enable_logging'</span>] <span class="hljs-keyword">else</span> <span class="hljs-string">'Disabled'</span>}</span>"</span>)
</code></pre>
<p><strong>Note the difference</strong>: <code>json.loads()</code> parses strings, while <code>json.load()</code> reads from files.</p>
<p>The <code>with</code> statement ensures that the file closes properly even if an error occurs during reading. After the <code>with</code> block completes, you have a Python dictionary containing all the parsed configuration data.</p>
<pre><code class="lang-plaintext">API URL: https://api.example.com/v2
Timeout: 30 seconds
Logging: Enabled
</code></pre>
<h2 id="heading-how-to-handle-json-parsing-errors">How to Handle JSON Parsing Errors</h2>
<p>JSON parsing can fail for many reasons: malformed syntax, unexpected data types, corrupted files, or network issues when fetching from APIs. Your code must handle these errors gracefully rather than crashing.</p>
<p>The <code>json</code> module raises a <code>JSONDecodeError</code> when it runs into invalid JSON. Here's how to catch and handle these errors appropriately.</p>
<p>The <code>try-except</code> block catches any JSON parsing errors:</p>
<ul>
<li><p>The <code>JSONDecodeError</code> exception provides detailed information about what went wrong: <code>e.msg</code> describes the error, <code>e.lineno</code> indicates which line contains the problem, and <code>e.colno</code> shows the character position. This information helps you debug malformed JSON quickly.</p>
</li>
<li><p>The function returns <code>None</code> when parsing fails, allowing calling code to check for this and handle the error appropriately.</p>
</li>
</ul>
<p>Let's test this with a few JSON examples:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Missing closing quote</span>
bad_json1 = <span class="hljs-string">'{"name": "Sarah, "age": 28}'</span>
result1 = parse_json_safely(bad_json1)
print(<span class="hljs-string">f"Result 1: <span class="hljs-subst">{result1}</span>\n"</span>)

<span class="hljs-comment"># Missing closing brace</span>
bad_json2 = <span class="hljs-string">'{"name": "Sarah", "age": 28'</span>
result2 = parse_json_safely(bad_json2)
print(<span class="hljs-string">f"Result 2: <span class="hljs-subst">{result2}</span>\n"</span>)

<span class="hljs-comment"># Extra comma</span>
bad_json3 = <span class="hljs-string">'{"name": "Sarah", "age": 28,}'</span>
result3 = parse_json_safely(bad_json3)
print(<span class="hljs-string">f"Result 3: <span class="hljs-subst">{result3}</span>\n"</span>)

<span class="hljs-comment"># Valid JSON for comparison</span>
good_json = <span class="hljs-string">'{"name": "Sarah", "age": 28}'</span>
result4 = parse_json_safely(good_json)
print(<span class="hljs-string">f"Result 4: <span class="hljs-subst">{result4}</span>"</span>)
</code></pre>
<p>Each malformed JSON string triggers a different error message indicating the specific syntax problem. The error messages help pinpoint exactly where the JSON is invalid. The final example shows that valid JSON parses successfully and returns a dictionary instead of <code>None</code>.</p>
<pre><code class="lang-plaintext">JSON parsing failed: Expecting ',' delimiter
Error at line 1, column 19
Result 1: None

JSON parsing failed: Expecting ',' delimiter
Error at line 1, column 28
Result 2: None

JSON parsing failed: Expecting property name enclosed in double quotes
Error at line 1, column 29
Result 3: None

Result 4: {'name': 'Sarah', 'age': 28}
</code></pre>
<p>When reading JSON files, you should also handle file-related errors. The following function <code>load_json_file_safely</code> handles three types of errors:</p>
<ul>
<li><p><code>FileNotFoundError</code> when the file doesn't exist,</p>
</li>
<li><p><code>PermissionError</code> when the application can't read the file, and</p>
</li>
<li><p><code>JSONDecodeError</code> when the file contains invalid JSON. Each error type gets its own except block with an appropriate message.</p>
</li>
</ul>
<p>The calling code checks if the result is <code>None</code> and falls back to default values, ensuring the application continues running even when the file can't be loaded.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> json

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_json_file_safely</span>(<span class="hljs-params">filepath</span>):</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">with</span> open(filepath, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> f:
            <span class="hljs-keyword">return</span> json.load(f)
    <span class="hljs-keyword">except</span> FileNotFoundError:
        print(<span class="hljs-string">f"Error: File '<span class="hljs-subst">{filepath}</span>' not found"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
    <span class="hljs-keyword">except</span> PermissionError:
        print(<span class="hljs-string">f"Error: Permission denied reading '<span class="hljs-subst">{filepath}</span>'"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
    <span class="hljs-keyword">except</span> json.JSONDecodeError <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error: Invalid JSON in '<span class="hljs-subst">{filepath}</span>'"</span>)
        print(<span class="hljs-string">f"  <span class="hljs-subst">{e.msg}</span> at line <span class="hljs-subst">{e.lineno}</span>"</span>)
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

data = load_json_file_safely(<span class="hljs-string">'missing_file.json'</span>)
<span class="hljs-keyword">if</span> data <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span>:
    print(<span class="hljs-string">"Using default configuration"</span>)
    data = {<span class="hljs-string">"timeout"</span>: <span class="hljs-number">30</span>, <span class="hljs-string">"retries"</span>: <span class="hljs-number">3</span>}
</code></pre>
<p>If you run the above code, you’ll get the following output:</p>
<pre><code class="lang-plaintext">Error: File 'missing_file.json' not found
Using default configuration
</code></pre>
<p>And that’s a wrap! Thank you for making it this far if you’ve following along! 🥳</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>The <code>json</code> module provides everything you need for working with JSON data in Python. Here’s a summary of what we covered:</p>
<ul>
<li><p>The core functions handle the most common operations: <code>json.loads()</code> parses JSON strings into Python objects, and <code>json.load()</code> reads and parses JSON from files.</p>
</li>
<li><p>JSON parsing automatically converts between JSON and Python data types. This conversion lets you work with parsed JSON using standard Python syntax.</p>
</li>
<li><p>You can navigate nested JSON by chaining dictionary keys and list indices together. Access nested values like <code>data['section']['subsection']['field']</code> by following the structure down through each level.</p>
</li>
<li><p>Always wrap JSON parsing in <code>try-except</code> blocks when working with external data. The <code>JSONDecodeError</code> exception provides specific information about parsing failures including the error location, helping you debug issues quickly. When reading files, also catch <code>FileNotFoundError</code> and <code>PermissionError</code> to handle common file access problems gracefully.</p>
</li>
</ul>
<p>Get comfortable with these fundamentals and you'll be able to handle most JSON parsing tasks you’ll need for your Python projects. Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Work with TOML Files in Python ]]>
                </title>
                <description>
                    <![CDATA[ TOML (Tom's Obvious Minimal Language) has become the modern standard for configuration files in Python projects. It's more expressive than INI files and cleaner than JSON or YAML. Since Python 3.11, the standard library includes the tomllib module fo... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-toml-files-in-python/</link>
                <guid isPermaLink="false">68fbd8942f09b776064b952c</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Fri, 24 Oct 2025 19:50:44 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761335431619/2d4fefec-cdbb-4146-ae9c-24468b483278.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>TOML (Tom's Obvious Minimal Language) has become the modern standard for configuration files in Python projects. It's more expressive than INI files and cleaner than JSON or YAML.</p>
<p>Since Python 3.11, the standard library includes the <a target="_blank" href="https://docs.python.org/3/library/tomllib.html">tomllib</a> module for reading and parsing TOML files. TOML offers several advantages over other configuration formats. It supports complex data types like arrays and nested tables while remaining human-readable. Many Python projects, including <a target="_blank" href="https://python-poetry.org/">Poetry</a> and <a target="_blank" href="https://pypi.org/project/setuptools/">setuptools</a>, use <code>pyproject.toml</code> for configuration.</p>
<p>And in this tutorial, we’ll learn how to parse TOML files in Python.</p>
<p>🔗 <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/config-management-basics/parsing-toml-files"><strong>Here’s the code on GitHub</strong></a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you'll need:</p>
<ul>
<li><p><strong>Python 3.11 or higher</strong>: The <code>tomllib</code> module is part of the standard library starting from Python 3.11</p>
</li>
<li><p><strong>Basic Python knowledge</strong>: Familiarity with dictionaries, file I/O, and basic syntax</p>
</li>
<li><p><strong>A text editor or IDE</strong>: Any editor to create and edit TOML and Python files</p>
</li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-understanding-the-toml-format">Understanding the TOML Format</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-toml-files-with-tomllib">How to Read TOML Files with tomllib</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-work-with-toml-data-types">How to Work with TOML Data Types</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-a-toml-config-manager">How to Build a TOML Config Manager</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-handle-missing-values-safely">How to Handle Missing Values Safely</a></p>
</li>
</ol>
<h2 id="heading-understanding-the-toml-format">Understanding the TOML Format</h2>
<p>TOML files organize data into tables (similar to INI sections) but with more powerful features. Let's create a sample configuration to understand the syntax.</p>
<p>Create <code>config.toml</code>:</p>
<pre><code class="lang-plaintext"># Application configuration
title = "My Application"
version = "1.0.0"

[database]
host = "localhost"
port = 5432
username = "app_user"
password = "secure_password"
databases = ["myapp_db", "myapp_cache"]
pool_size = 10
ssl_enabled = true

[server]
host = "0.0.0.0"
port = 8000
debug = false
allowed_hosts = ["localhost", "127.0.0.1", "example.com"]

[logging]
level = "INFO"
format = "%(asctime)s - %(levelname)s - %(message)s"
handlers = ["console", "file"]

[cache]
enabled = true
ttl = 3600
max_size = 1000

[features]
enable_api = true
enable_webhooks = false
rate_limit = 100
</code></pre>
<p>This TOML file shows key features: simple key-value pairs, tables (sections in brackets), arrays (square brackets with comma-separated values), and different data types including strings, integers, booleans, and arrays.</p>
<h2 id="heading-how-to-read-toml-files-with-tomllib">How to Read TOML Files with <code>tomllib</code></h2>
<p>The <code>tomllib</code> module is part of Python's standard library starting from version 3.11. It provides a simple interface for loading TOML files like so:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> tomllib

<span class="hljs-keyword">with</span> open(<span class="hljs-string">'config.toml'</span>, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> f:
    config = tomllib.load(f)

<span class="hljs-comment"># Access values</span>
app_title = config[<span class="hljs-string">'title'</span>]
db_host = config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'host'</span>]
db_port = config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'port'</span>]

print(<span class="hljs-string">f"Application: <span class="hljs-subst">{app_title}</span>"</span>)
print(<span class="hljs-string">f"Database: <span class="hljs-subst">{db_host}</span>:<span class="hljs-subst">{db_port}</span>"</span>)
print(<span class="hljs-string">f"Config keys: <span class="hljs-subst">{config.keys()}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Application: My Application
Database: localhost:5432
Config keys: dict_keys(['title', 'version', 'database', 'server', 'logging', 'cache', 'features'])
</code></pre>
<p>Note that <code>tomllib</code> requires opening files in binary mode (<code>'rb'</code>). The <code>load()</code> function parses the TOML file and returns a regular Python dictionary.</p>
<p>Values are automatically converted to appropriate Python types: strings remain strings, integers become ints, booleans become True/False, and arrays become lists. Next, let’s take a closer look at working with different data types.</p>
<h2 id="heading-how-to-work-with-toml-data-types">How to Work with TOML Data Types</h2>
<p>TOML's type system maps cleanly to Python's built-in types. Here's how to work with different value types:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> tomllib

<span class="hljs-keyword">with</span> open(<span class="hljs-string">'config.toml'</span>, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> f:
    config = tomllib.load(f)

<span class="hljs-comment"># Strings</span>
app_title = config[<span class="hljs-string">'title'</span>]

<span class="hljs-comment"># Integers</span>
db_port = config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'port'</span>]
cache_ttl = config[<span class="hljs-string">'cache'</span>][<span class="hljs-string">'ttl'</span>]

<span class="hljs-comment"># Booleans</span>
debug_mode = config[<span class="hljs-string">'server'</span>][<span class="hljs-string">'debug'</span>]
cache_enabled = config[<span class="hljs-string">'cache'</span>][<span class="hljs-string">'enabled'</span>]

<span class="hljs-comment"># Arrays (become Python lists)</span>
databases = config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'databases'</span>]
allowed_hosts = config[<span class="hljs-string">'server'</span>][<span class="hljs-string">'allowed_hosts'</span>]

print(<span class="hljs-string">f"Databases: <span class="hljs-subst">{databases}</span>"</span>)
print(<span class="hljs-string">f"Type of databases: <span class="hljs-subst">{type(databases)}</span>"</span>)
print(<span class="hljs-string">f"Debug mode: <span class="hljs-subst">{debug_mode}</span>, type: <span class="hljs-subst">{type(debug_mode)}</span>"</span>)
</code></pre>
<p>With <code>tomllib</code>, you don't need special getter methods like <code>ConfigParser</code>. The returned dictionary contains properly typed Python objects ready to use as seen:</p>
<pre><code class="lang-plaintext">Databases: ['myapp_db', 'myapp_cache']
Type of databases: &lt;class 'list'&gt;
Debug mode: False, type: &lt;class 'bool'&gt;
</code></pre>
<h2 id="heading-how-to-build-a-toml-config-manager">How to Build a TOML Config Manager</h2>
<p>For production applications, wrapping TOML loading in a configuration class provides better error handling and validation. Here’s how you can do it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> tomllib
<span class="hljs-keyword">from</span> pathlib <span class="hljs-keyword">import</span> Path

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TOMLConfig</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, config_file=<span class="hljs-string">'config.toml'</span></span>):</span>
        self.config_file = Path(config_file)

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> self.config_file.exists():
            <span class="hljs-keyword">raise</span> FileNotFoundError(<span class="hljs-string">f"Config file not found: <span class="hljs-subst">{config_file}</span>"</span>)

        <span class="hljs-keyword">with</span> open(self.config_file, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> f:
            self.config = tomllib.load(f)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span>(<span class="hljs-params">self, key, default=None</span>):</span>
        <span class="hljs-string">"""Get a top-level configuration value"""</span>
        <span class="hljs-keyword">return</span> self.config.get(key, default)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_section</span>(<span class="hljs-params">self, section</span>):</span>
        <span class="hljs-string">"""Get an entire configuration section"""</span>
        <span class="hljs-keyword">if</span> section <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> self.config:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">f"Section '<span class="hljs-subst">{section}</span>' not found"</span>)
        <span class="hljs-keyword">return</span> self.config[section]
</code></pre>
<p>You can use the <code>TOMLConfig</code> class like so:</p>
<pre><code class="lang-python">config = TOMLConfig(<span class="hljs-string">'config.toml'</span>)

<span class="hljs-comment"># Get top-level values</span>
app_title = config.get(<span class="hljs-string">'title'</span>)
version = config.get(<span class="hljs-string">'version'</span>)

<span class="hljs-comment"># Get entire sections</span>
db_config = config.get_section(<span class="hljs-string">'database'</span>)
server_config = config.get_section(<span class="hljs-string">'server'</span>)

print(<span class="hljs-string">f"<span class="hljs-subst">{app_title}</span> v<span class="hljs-subst">{version}</span>"</span>)
print(<span class="hljs-string">f"Database config: <span class="hljs-subst">{db_config}</span>"</span>)
</code></pre>
<p>This configuration class provides a clean interface to your TOML file. It validates that the file exists before trying to parse it and provides methods to safely access configuration values.</p>
<p>Running the above code gives this output:</p>
<pre><code class="lang-plaintext">My Application v1.0.0
Database config: {'host': 'localhost', 'port': 5432, 'username': 'app_user', 'password': 'secure_password', 'databases': ['myapp_db', 'myapp_cache'], 'pool_size': 10, 'ssl_enabled': True}
</code></pre>
<h2 id="heading-how-to-handle-missing-values-safely">How to Handle Missing Values Safely</h2>
<p>Your code needs to handle missing configuration gracefully. Here's how to provide defaults and validate required values:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> tomllib

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_config_safe</span>(<span class="hljs-params">config_file=<span class="hljs-string">'config.toml'</span></span>):</span>
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">with</span> open(config_file, <span class="hljs-string">'rb'</span>) <span class="hljs-keyword">as</span> f:
            <span class="hljs-keyword">return</span> tomllib.load(f)
    <span class="hljs-keyword">except</span> FileNotFoundError:
        print(<span class="hljs-string">f"Config file <span class="hljs-subst">{config_file}</span> not found, using defaults"</span>)
        <span class="hljs-keyword">return</span> {}
    <span class="hljs-keyword">except</span> tomllib.TOMLDecodeError <span class="hljs-keyword">as</span> e:
        print(<span class="hljs-string">f"Error parsing TOML: <span class="hljs-subst">{e}</span>"</span>)
        <span class="hljs-keyword">raise</span>

config = load_config_safe(<span class="hljs-string">'config.toml'</span>)

<span class="hljs-comment"># Get with defaults</span>
db_host = config.get(<span class="hljs-string">'database'</span>, {}).get(<span class="hljs-string">'host'</span>, <span class="hljs-string">'localhost'</span>)
db_port = config.get(<span class="hljs-string">'database'</span>, {}).get(<span class="hljs-string">'port'</span>, <span class="hljs-number">5432</span>)
debug = config.get(<span class="hljs-string">'server'</span>, {}).get(<span class="hljs-string">'debug'</span>, <span class="hljs-literal">False</span>)

print(<span class="hljs-string">f"Database: <span class="hljs-subst">{db_host}</span>:<span class="hljs-subst">{db_port}</span>"</span>)
print(<span class="hljs-string">f"Debug: <span class="hljs-subst">{debug}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Database: localhost:5432
Debug: False
</code></pre>
<p>This pattern uses chained <code>.get()</code> calls with defaults. If a section or key doesn't exist, you get the default value instead of a KeyError.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When working with TOML files in Python, follow these guidelines:</p>
<ul>
<li><p>Always open in binary mode: The <code>tomllib</code> module requires binary mode (<code>'rb'</code>) when opening files.</p>
</li>
<li><p>Use nested tables for organization: Take advantage of TOML's ability to nest tables for complex configurations.</p>
</li>
<li><p>Provide defaults for optional settings: Use <code>.get()</code> with default values to make your application more flexible.</p>
</li>
</ul>
<p>Consider using TOML for new projects. If you're starting fresh, TOML is a great choice for Python configuration. Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Parse INI Config Files in Python with Configparser ]]>
                </title>
                <description>
                    <![CDATA[ Configuration files provide a structured way to manage application settings that's more organized than environment variables alone. INI files, short for initialization files, with their simple section-based format, are both easy to read and parse. Py... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-parse-ini-config-files-in-python-with-configparser/</link>
                <guid isPermaLink="false">68f25c6d7d9fdcba5704db2e</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Fri, 17 Oct 2025 15:10:37 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1760712555277/4eabf2d7-fa9d-445b-8e0a-6ebdee53790c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Configuration files provide a structured way to manage application settings that's more organized than environment variables alone.</p>
<p>INI files, short for initialization files, with their simple section-based format, are both easy to read and parse. Python's built-in <a target="_blank" href="https://docs.python.org/3/library/configparser.html">configparser module</a> makes working with these files straightforward and powerful.</p>
<p>This tutorial will teach you how to read and parse such <code>.ini</code> config files using the <code>configparser</code> module.</p>
<p>🔗 <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/config-management-basics/parsing-ini-files"><strong>Here’s the code on GitHub</strong></a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>To follow along with this tutorial, you should have:</p>
<ul>
<li><p>Python 3.7 or later installed on your system</p>
</li>
<li><p>Basic understanding of Python syntax and data structures (dictionaries, strings)</p>
</li>
<li><p>Familiarity with file operations in Python</p>
</li>
<li><p>A text editor or IDE for writing Python code</p>
</li>
<li><p>Basic knowledge of configuration files and why they're used in applications</p>
</li>
</ul>
<p>No external packages are required, as we'll be using Python's built-in <code>configparser</code> module.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-understanding-the-ini-file-format">Understanding the INI File Format</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-basic-configparser-usage">Basic ConfigParser Usage</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-type-conversion-and-default-values">Type Conversion and Default Values</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-simple-config-manager">How to Create a Simple Config Manager</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-work-with-multiple-sections-in-ini-files">How to Work with Multiple Sections in INI Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-configuration-files">How to Write Configuration Files</a></p>
</li>
</ol>
<h2 id="heading-understanding-the-ini-file-format">Understanding the INI File Format</h2>
<p>INI files organize configuration into sections, where each section contains key-value pairs. This structure is useful for applications with multiple components or environments. Let's look at what an INI file looks like before we parse it.</p>
<p>Create a file named <code>app.ini</code>:</p>
<pre><code class="lang-plaintext">[database]
host = localhost
port = 5432
username = app_user
password = secure_password
pool_size = 10
ssl_enabled = true

[server]
host = 0.0.0.0
port = 8000
debug = false

[logging]
level = INFO
file = app.log
</code></pre>
<p>This file contains three sections: database, server, and logging. Each section groups related settings together, making the configuration easy to understand and maintain.</p>
<h2 id="heading-basic-configparser-usage">Basic ConfigParser Usage</h2>
<p>The <code>configparser</code> module provides the <code>ConfigParser</code> class, which handles all the parsing work. Here's how to read and access configuration values:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> configparser

config = configparser.ConfigParser()
config.read(<span class="hljs-string">'app.ini'</span>)

<span class="hljs-comment"># Access values from sections</span>
db_host = config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'host'</span>]
db_port = config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'port'</span>]

print(<span class="hljs-string">f"Database: <span class="hljs-subst">{db_host}</span>:<span class="hljs-subst">{db_port}</span>"</span>)
print(<span class="hljs-string">f"Sections: <span class="hljs-subst">{config.sections()}</span>"</span>)
</code></pre>
<p>This code shows the basic workflow:</p>
<ul>
<li><p>create a <code>ConfigParser</code> object,</p>
</li>
<li><p>read your INI file,</p>
</li>
<li><p>then access values using dictionary-like syntax.</p>
</li>
</ul>
<p>The first bracket contains the section name, and the second contains the key.</p>
<p>Create the <code>app.ini</code> file and run the above code. You should see the following output:</p>
<pre><code class="lang-plaintext">Database: localhost:5432
Sections: ['database', 'server', 'logging']
</code></pre>
<h2 id="heading-type-conversion-and-default-values">Type Conversion and Default Values</h2>
<p>Configuration values in INI files are stored as strings, but you often need them as integers, booleans, or floats. <code>ConfigParser</code> provides convenient methods for type conversion as shown here:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> configparser

config = configparser.ConfigParser()
config.read(<span class="hljs-string">'app.ini'</span>)

<span class="hljs-comment"># Automatic type conversion</span>
db_port = config.getint(<span class="hljs-string">'database'</span>, <span class="hljs-string">'port'</span>)
ssl_enabled = config.getboolean(<span class="hljs-string">'database'</span>, <span class="hljs-string">'ssl_enabled'</span>)

<span class="hljs-comment"># With fallback defaults</span>
max_retries = config.getint(<span class="hljs-string">'database'</span>, <span class="hljs-string">'max_retries'</span>, fallback=<span class="hljs-number">3</span>)
timeout = config.getfloat(<span class="hljs-string">'database'</span>, <span class="hljs-string">'timeout'</span>, fallback=<span class="hljs-number">30.0</span>)

print(<span class="hljs-string">f"Port: <span class="hljs-subst">{db_port}</span>, SSL: <span class="hljs-subst">{ssl_enabled}</span>"</span>)
</code></pre>
<p>In this code, the <code>getint()</code>, <code>getboolean()</code>, and <code>getfloat()</code> methods convert string values to the appropriate type. The <code>fallback</code> parameter provides a default value when the key doesn't exist, preventing errors.</p>
<p>When you run the above code, you’ll get:</p>
<pre><code class="lang-plaintext">Port: 5432, SSL: True
</code></pre>
<h2 id="heading-how-to-create-a-simple-config-manager">How to Create a Simple Config Manager</h2>
<p>A practical approach is to wrap <code>ConfigParser</code> in a class that validates configuration and provides easy access to settings:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> configparser
<span class="hljs-keyword">from</span> pathlib <span class="hljs-keyword">import</span> Path

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConfigManager</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, config_file=<span class="hljs-string">'app.ini'</span></span>):</span>
        self.config = configparser.ConfigParser()

        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> Path(config_file).exists():
            <span class="hljs-keyword">raise</span> FileNotFoundError(<span class="hljs-string">f"Config file not found: <span class="hljs-subst">{config_file}</span>"</span>)

        self.config.read(config_file)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_database_config</span>(<span class="hljs-params">self</span>):</span>
        db = self.config[<span class="hljs-string">'database'</span>]
        <span class="hljs-keyword">return</span> {
            <span class="hljs-string">'host'</span>: db.get(<span class="hljs-string">'host'</span>),
            <span class="hljs-string">'port'</span>: db.getint(<span class="hljs-string">'port'</span>),
            <span class="hljs-string">'username'</span>: db.get(<span class="hljs-string">'username'</span>),
            <span class="hljs-string">'password'</span>: db.get(<span class="hljs-string">'password'</span>),
            <span class="hljs-string">'pool_size'</span>: db.getint(<span class="hljs-string">'pool_size'</span>, fallback=<span class="hljs-number">5</span>)
        }
</code></pre>
<p>This manager class validates that the file exists and provides clean methods to access configuration. It returns dictionaries with properly typed values.</p>
<p>And you can use it like so:</p>
<pre><code class="lang-python">config = ConfigManager(<span class="hljs-string">'app.ini'</span>)
db_config = config.get_database_config()
print(db_config)
</code></pre>
<p>This outputs:</p>
<pre><code class="lang-plaintext">{'host': 'localhost', 'port': 5432, 'username': 'app_user', 'password': 'secure_password', 'pool_size': 10}
</code></pre>
<h2 id="heading-how-to-work-with-multiple-sections-in-ini-files">How to Work with Multiple Sections in INI Files</h2>
<p>You can organize different parts of your application into separate sections and access them independently:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> configparser

config = configparser.ConfigParser()
config.read(<span class="hljs-string">'app.ini'</span>)

<span class="hljs-comment"># Get all options in a section as a dictionary</span>
db_settings = dict(config[<span class="hljs-string">'database'</span>])
server_settings = dict(config[<span class="hljs-string">'server'</span>])

<span class="hljs-comment"># Check if a section exists</span>
<span class="hljs-keyword">if</span> config.has_section(<span class="hljs-string">'cache'</span>):
    cache_enabled = config.getboolean(<span class="hljs-string">'cache'</span>, <span class="hljs-string">'enabled'</span>)
<span class="hljs-keyword">else</span>:
    cache_enabled = <span class="hljs-literal">False</span>

print(<span class="hljs-string">f"Database settings: <span class="hljs-subst">{db_settings}</span>"</span>)
print(<span class="hljs-string">f"Caching enabled: <span class="hljs-subst">{cache_enabled}</span>"</span>)
</code></pre>
<p>The <code>dict()</code> conversion gives you all key-value pairs from a section at once. The <code>has_section()</code> method lets you conditionally handle optional configuration sections.</p>
<p>Running the above code should give you the following output:</p>
<pre><code class="lang-plaintext">Database settings: {'host': 'localhost', 'port': '5432', 'username': 'app_user', 'password': 'secure_password', 'pool_size': '10', 'ssl_enabled': 'true'}
Caching enabled: False
</code></pre>
<h2 id="heading-how-to-write-configuration-files">How to Write Configuration Files</h2>
<p><code>ConfigParser</code> can also create and modify INI files, which is useful for saving user preferences or generating config templates:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> configparser

config = configparser.ConfigParser()

<span class="hljs-comment"># Add sections and values</span>
config[<span class="hljs-string">'database'</span>] = {
    <span class="hljs-string">'host'</span>: <span class="hljs-string">'localhost'</span>,
    <span class="hljs-string">'port'</span>: <span class="hljs-string">'5432'</span>,
    <span class="hljs-string">'username'</span>: <span class="hljs-string">'myapp'</span>
}

config[<span class="hljs-string">'server'</span>] = {
    <span class="hljs-string">'host'</span>: <span class="hljs-string">'0.0.0.0'</span>,
    <span class="hljs-string">'port'</span>: <span class="hljs-string">'8000'</span>,
    <span class="hljs-string">'debug'</span>: <span class="hljs-string">'false'</span>
}

<span class="hljs-comment"># Write to file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'generated.ini'</span>, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> configfile:
    config.write(configfile)

print(<span class="hljs-string">"Configuration file created!"</span>)
</code></pre>
<p>This code creates a new INI file from scratch. The write() method saves the configuration in the proper INI format with sections and key-value pairs.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>When environment variables aren't enough and you need grouped settings for different components, INI files are your answer.</p>
<p>The format is human-readable, <code>ConfigParser</code> handles type conversion automatically, and it's built into Python's standard library. Wrap it in a configuration class for validation and clean access patterns.</p>
<p>Also remember:</p>
<ul>
<li><p>Organize by component. Use sections to group related settings.</p>
</li>
<li><p>Use type conversion methods. Always use <code>getint()</code>, <code>getboolean()</code>, and <code>getfloat()</code> rather than manual conversion. They handle edge cases better.</p>
</li>
<li><p>Provide sensible defaults. Use the <code>fallback</code> parameter for optional settings so your application works with minimal configuration.</p>
</li>
<li><p>Validate early. Check that required sections and keys exist at startup before attempting to use them.</p>
</li>
<li><p>Keep secrets separate. Don't commit INI files with passwords to version control. Use <code>.ini.example</code> files with dummy values as templates.</p>
</li>
</ul>
<p>In the next article, you’ll learn how to work with TOML files in Python. Until then, keep coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
