<?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" version="2.0"><channel><title><![CDATA[Shreyas Taware Tech Blog's]]></title><description><![CDATA[Shreyas Taware Tech Blog's]]></description><link>https://hashnode.shreyastaware.me/blog</link><generator>RSS for Node</generator><lastBuildDate>Sat, 07 Mar 2026 09:08:11 GMT</lastBuildDate><atom:link href="https://hashnode.shreyastaware.me/blog/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><atom:link rel="first" href="https://hashnode.shreyastaware.me/blog/rss.xml"/><item><title><![CDATA[SOLID Principles + OOPs Concepts Explained using Python]]></title><description><![CDATA[<p>SOLID Principles and OOPs are a fundamental concept asked in all junior- to mid- level interviews. You need to not only know the definitions of these, but you also should be able to write code which follows these principles, and give real-world examples of these wherever necessary.</p>
<p>This blog is an attempt to do just that.</p>
<p><strong>SOLID</strong> stands for</p>
<ol>
<li><p>Single Responsibility Principle (SRP)</p>
</li>
<li><p>Open-Closed Principle (OCP)</p>
</li>
<li><p>Liskov Substitution Principle (LSP)</p>
</li>
<li><p>Interface Segregation Principle (ISP)</p>
</li>
<li><p>Dependency Inversion Principle (DIP)</p>
</li>
</ol>
<p>Let us check out these principles one-by-one.</p>
<h2 id="heading-single-responsibility-principle-srp">Single Responsibility Principle (SRP)</h2>
<p>This principles states that <em>A class should have only one reason to change.</em></p>
<p>This means a class should be responsible for a single, specific piece of functionality.</p>
<p>Take the example of an Authentication Service versus a User Profile Service. Commonly used by almost all companies which serve Software as a Service to users.</p>
<p> <strong>SRP Violation (what beginners often do):</strong> Put all User Details &amp; Auth in a single class</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">login</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">logout</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">reset_password</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_profile</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">upload_avatar</span>(<span class="hljs-params">self</span>):</span> ...
</code></pre>
<p><strong>Problem:</strong><br />This class changes for <strong>multiple reasons</strong>:</p>
<ul>
<li><p>Auth rules change (OAuth, MFA, tokens)</p>
</li>
<li><p>Profile rules change (new fields, avatars, preferences)</p>
</li>
</ul>
<p> <strong>SRP Applied (real startup architecture):</strong> Separate classes based on specific functionality (Authentication vs User Profile Details)</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AuthService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">login</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">logout</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">reset_password</span>(<span class="hljs-params">self</span>):</span> ...

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserProfileService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_profile</span>(<span class="hljs-params">self</span>):</span> ...
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">upload_avatar</span>(<span class="hljs-params">self</span>):</span> ...
</code></pre>
<h2 id="heading-open-closed-principle-ocp">Open/ Closed Principle (OCP)</h2>
<p>This principle states that <em>A software entity (like a class, module, or function) should be open for extension, but closed for modification.</em></p>
<p>You should be able to add new functionality without changing existing code.</p>
<p><strong>Real World Example:</strong> Imagine an early-stage product that only supports card payments. Someone writes this:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount, method</span>):</span>
        <span class="hljs-keyword">if</span> method == <span class="hljs-string">"card"</span>:
            <span class="hljs-keyword">return</span> self.pay_by_card(amount)
        <span class="hljs-keyword">elif</span> method == <span class="hljs-string">"upi"</span>:
            <span class="hljs-keyword">return</span> self.pay_by_upi(amount)
        <span class="hljs-keyword">elif</span> method == <span class="hljs-string">"wallet"</span>:
            <span class="hljs-keyword">return</span> self.pay_by_wallet(amount)
</code></pre>
<p>Now when the business expands new payments will start coming. International Payments, Net Banking, Crypto. And because of the current structure, we would need to add a new method. Every single time a new method is added, this class has to be edited. That means touching code that already works, retesting everything, and hoping nothing breaks.</p>
<p>This is exactly the situation companies like Stripe design around, because payment code is too sensitive to keep rewriting. So the design shifts. Instead of modifying the same method again and again, the system becomes extensible:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentMethod</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">raise</span> NotImplementedError


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CardPayment</span>(<span class="hljs-params">PaymentMethod</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount</span>):</span>
        ...


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UpiPayment</span>(<span class="hljs-params">PaymentMethod</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount</span>):</span>
        ...


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WalletPayment</span>(<span class="hljs-params">PaymentMethod</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount</span>):</span>
        ...
</code></pre>
<p>And now the Service looks like:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount, payment_method: PaymentMethod</span>):</span>
        <span class="hljs-keyword">return</span> payment_method.pay(amount)
</code></pre>
<p>Whats important here is not inheritance itself. Its the fact that adding a new payment method no longer requires modifying <code>PaymentService</code>. You extend the system by adding new classes, not by reopening old ones. Thats OCP working in a very real, revenue-protecting way.</p>
<h2 id="heading-liskov-substitution-principle-lsp"><strong>Liskov Substitution Principle (LSP)</strong></h2>
<p>This principle states that <em>The objects of a superclass should be replaceable with objects of its subclasses without affecting the correctness of the program</em>. The use of this principle commonly occurs during file handling, storage, and cloud integrations.</p>
<p>Imagine this base class:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FileStorage</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self, filename, data</span>):</span>
        <span class="hljs-keyword">pass</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete</span>(<span class="hljs-params">self, filename</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>Your code assumes that if a file can be saved, it can also be deleted. Thats a reasonable assumption.</p>
<p>Now someone adds a read-only storage backend, maybe for compliance or audit logs:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadOnlyStorage</span>(<span class="hljs-params">FileStorage</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self, filename, data</span>):</span>
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Storage is read-only"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete</span>(<span class="hljs-params">self, filename</span>):</span>
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Storage is read-only"</span>)
</code></pre>
<p>Again, this compiles fine. But it violates LSP in a very sneaky way.</p>
<p>Any code that works with <code>FileStorage</code> and reasonably expects save and delete to work will now break at runtime. The subclass is saying, I am a FileStorage, but behaving like one that cant actually do what FileStorage promises.</p>
<p>This kind of mistake shows up in systems built on top of platforms like Amazon S3 integrations, where some buckets or backends are intentionally immutable.</p>
<p>The fix is not defensive <code>try/except</code> everywhere. The fix is modeling reality correctly.</p>
<p>You separate the abstraction:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ReadableStorage</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">read</span>(<span class="hljs-params">self, filename</span>):</span>
        <span class="hljs-keyword">pass</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WritableStorage</span>(<span class="hljs-params">ReadableStorage</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self, filename, data</span>):</span>
        <span class="hljs-keyword">pass</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete</span>(<span class="hljs-params">self, filename</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>Now a read-only store is a <code>ReadableStorage</code>, not a <code>WritableStorage</code>. Nothing pretends to support behavior it cannot guarantee. Substitution becomes safe again.</p>
<h2 id="heading-interface-segregation-principle-isp"><strong>Interface Segregation Principle (ISP)</strong></h2>
<p>This principle states that <em>no client should be forced to depend on methods it does not use.</em></p>
<p>Let us take an example which is extremely common in internal tools, admin panels, and dashboards.</p>
<p>Imagine an interface for a user in an admin system:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AdminUserActions</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_user</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_user</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">view_reports</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">export_data</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>At first, this makes sense for a super-admin. Then the company hires support staff. They only need to view users and reports. Now you get something like this:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SupportAgent</span>(<span class="hljs-params">AdminUserActions</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_user</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Not allowed"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_user</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">raise</span> Exception(<span class="hljs-string">"Not allowed"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">view_reports</span>(<span class="hljs-params">self</span>):</span>
        ...

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">export_data</span>(<span class="hljs-params">self</span>):</span>
        ...
</code></pre>
<p>This design is screaming for trouble. Permission checks are scattered everywhere, and not allowed becomes a runtime surprise instead of a design guarantee.</p>
<p>Large platforms like Shopify handle this by separating responsibilities at the interface level, not by stuffing permissions into one giant abstraction.</p>
<p>The design moves toward something like:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserManagement</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_user</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_user</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Reporting</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">view_reports</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">export_data</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>Now a support agent depends only on <code>Reporting</code>. An admin depends on both. No one implements methods they arent supposed to use, and permission logic becomes explicit instead of implicit.</p>
<h2 id="heading-dependency-inversion-principle-dip"><strong>Dependency Inversion Principle (DIP)</strong></h2>
<p>This principle states that <em>High-level modules should not depend on low-level modules.</em> Both should depend on abstractions.</p>
<p>Imagine an order service in an e-commerce app:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderService</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.db = MySQLDatabase()

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_order</span>(<span class="hljs-params">self, order</span>):</span>
        self.db.save(order)
</code></pre>
<p>This looks innocent. It works. Orders get saved.</p>
<p>Then reality hits. The startup grows. Someone wants to move to PostgreSQL. Or to DynamoDB. Or add a cache. Suddenly <code>OrderService</code> is tightly bound to <code>MySQLDatabase</code>. Every infrastructure change forces you to modify core business logic.</p>
<p>Companies like Amazon ran into this problem at massive scale. Their business logic cannot care whether data is stored in MySQL, DynamoDB, or something custom.</p>
<p>So the dependency is inverted:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderRepository</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self, order</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>Now <code>OrderService</code> depends on this abstraction:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderService</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, repository: OrderRepository</span>):</span>
        self.repository = repository

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_order</span>(<span class="hljs-params">self, order</span>):</span>
        self.repository.save(order)
</code></pre>
<p>And the concrete database lives at the edge:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MySQLOrderRepository</span>(<span class="hljs-params">OrderRepository</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self, order</span>):</span>
        ...
</code></pre>
<p>What changed here is subtle but powerful. The high-level code no longer knows or cares about databases. Infrastructure details depend on the business abstraction, not the other way around. That inversion is the core of DIP.</p>
<hr />
<p>Now, let us move on to OOPs concepts.</p>
<p>OOP stands for Object-Oriented Programming. There are 4 concepts in Object-Oriented Programming namely:</p>
<ol>
<li><p>Abstraction</p>
</li>
<li><p>Encapsulation</p>
</li>
<li><p>Inheritance</p>
</li>
<li><p>Polymorphism</p>
</li>
</ol>
<p>Let us start with Abstraction.</p>
<h2 id="heading-abstraction">Abstraction</h2>
<p>Abstraction involves hiding complex implementation details and showing only the necessary features of an object. In real codebases, abstraction exists to reduce cognitive load. You dont want every part of the system to understand every detail.</p>
<p>A very common abstraction shows up in databases.</p>
<p>Imagine writing business logic that directly uses SQL everywhere:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_order</span>(<span class="hljs-params">order</span>):</span>
    cursor.execute(
        <span class="hljs-string">"INSERT INTO orders (id, total) VALUES (?, ?)"</span>,
        (order.id, order.total)
    )
</code></pre>
<p>This works, but now your business logic knows too much. It knows SQL. It knows table names. It knows schema details. When the database changes, business logic breaks.</p>
<p>So teams introduce an abstraction:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">OrderRepository</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save</span>(<span class="hljs-params">self, order</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>Now the business code talks only to the abstraction:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_order</span>(<span class="hljs-params">order, repo: OrderRepository</span>):</span>
    repo.save(order)
</code></pre>
<p>The database details disappear behind the abstraction. Whether the data goes to MySQL, PostgreSQL, or DynamoDB becomes irrelevant to the caller. This is abstraction doing its job: shielding the rest of the system from complexity.</p>
<h2 id="heading-encapsulation">Encapsulation</h2>
<p>Encapsulation is the bundling of data and the methods that operate on that data within a single unit (class). It restricts direct access to some of an object's components, which is a means of preventing accidental interference and misuse of the methods and data. It exists because uncontrolled access to data always turns into bugs.</p>
<p>Consider a naive bank account class:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BankAccount</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.balance = <span class="hljs-number">0</span>
</code></pre>
<p>Anyone can now do this:</p>
<pre><code class="lang-python">account.balance = <span class="hljs-number">-100000</span>
</code></pre>
<p>Nothing stops it. No rules are enforced. This is how systems quietly rot.</p>
<p>Encapsulation fixes this by controlling access:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BankAccount</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._balance = <span class="hljs-number">0</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">deposit</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">if</span> amount &lt;= <span class="hljs-number">0</span>:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Invalid deposit"</span>)
        self._balance += amount

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">withdraw</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">if</span> amount &gt; self._balance:
            <span class="hljs-keyword">raise</span> ValueError(<span class="hljs-string">"Insufficient funds"</span>)
        self._balance -= amount
</code></pre>
<p>Now the state can only change through valid operations. The rules live close to the data they protect. This pattern shows up everywhere in financial systems because money without guardrails is dangerous.</p>
<h2 id="heading-inheritance">Inheritance</h2>
<p>Inheritance allows a class to inherit properties and methods from another class, promoting code reuse and establishing a relationship between parent and child classes.</p>
<p>Inheritance appears in domain modeling. You might have a base <code>Employee</code>:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Employee</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calculate_salary</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>Then specializations:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FullTimeEmployee</span>(<span class="hljs-params">Employee</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calculate_salary</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.base_salary

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Contractor</span>(<span class="hljs-params">Employee</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">calculate_salary</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.hours * self.rate
</code></pre>
<p>Payroll systems rely on this kind of inheritance because different employee types share identity but differ in behavior.</p>
<h2 id="heading-polymorphism">Polymorphism</h2>
<p>Polymorphism allows objects of different types to be treated as objects of a common base class. It enables a single interface to represent different underlying forms (data types).</p>
<p>A very real example is payment handling.</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PaymentMethod</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount</span>):</span>
        <span class="hljs-keyword">pass</span>
</code></pre>
<p>Different implementations:</p>
<pre><code class="lang-python"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CardPayment</span>(<span class="hljs-params">PaymentMethod</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount</span>):</span>
        print(<span class="hljs-string">"Paid by card"</span>)

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UPIPayment</span>(<span class="hljs-params">PaymentMethod</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">pay</span>(<span class="hljs-params">self, amount</span>):</span>
        print(<span class="hljs-string">"Paid by UPI"</span>)
</code></pre>
<p>Here <strong>pay</strong> method is implemented differently for <strong>CardPayment</strong> Class and <strong>UPIPayment</strong> Class though they inherit the same method from the <strong>PaymentMethod</strong> Class. As you can see, Polymorphism means the same call behaves differently depending on the object. The calling code does not care which one it gets:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">checkout</span>(<span class="hljs-params">method: PaymentMethod, amount</span>):</span>
    method.pay(amount)
</code></pre>
<p>This is polymorphism in its purest form, and companies like Stripe build entire platforms around this idea.</p>
<hr />
<p>And that is it, hope you had a clear idea of what SOLID Principles and OOPs concepts are and how they are used in real-world projects and implementations.</p>
<p>If you liked this blog, please write to me on shreyastaware.work@gmail.com. And for anything else, feel free to visit my website: <a target="_blank" href="https://shreyastaware.me/">shreyastaware.me</a></p>
<p>Until next time,</p>
<p>Shreyas</p>
]]></description><link>https://hashnode.shreyastaware.me/blog/solid-principles-oops-concepts-explained-using-python</link><guid isPermaLink="true">https://hashnode.shreyastaware.me/blog/solid-principles-oops-concepts-explained-using-python</guid><category><![CDATA[SOLID principles]]></category><category><![CDATA[OOPS]]></category><dc:creator><![CDATA[Shreyas Taware]]></dc:creator></item><item><title><![CDATA[Docker Commands to make your LIFE a BREEZE!]]></title><description><![CDATA[<p>Often times as a developer, you need to wrap your code in an isolated environment, so that you can test it on someone elses computer or deploy it on cloud.</p>
<p>That is when you need <a target="_blank" href="https://www.docker.com/">Docker</a>.</p>
<p>In this article, youll find common commands you would need while testing and deploying applications.</p>
<p>Refer to this article as a quick reference guide for commands you'll need to look up regularly.</p>
<h3 id="heading-build-a-docker-container">Build a docker container</h3>
<pre><code class="lang-bash">docker build -t &lt;image_name&gt; .
</code></pre>
<p>Key thing to note here is that a docker container runs in isolation to the rest of the operating system it is built on, but, it does need the same architecture in the operating system as a base to be rebuilt on.</p>
<p>So, for example, if a docker container was built on the x86 architecture pc, it would need any computer with a variant of this architecture to rebuild it. It would not work on the <strong><em>AArch64 architecture</em></strong> in the macOS mX series<strong><em>.</em></strong></p>
<p>You can also specify the operating system corresponding to how the image should be built:</p>
<pre><code class="lang-bash">docker build --platform=linux/amd64 -t &lt;image_name&gt; .
</code></pre>
<h3 id="heading-running-the-docker-container">Running the docker container</h3>
<pre><code class="lang-bash">docker run --name &lt;container_name&gt; &lt;image_name&gt;
</code></pre>
<p>In the above command, <strong>slt-container</strong> is the name of the docker container which contains the docker image <strong>slc-vnc-test</strong>.</p>
<h3 id="heading-checking-running-containers">Checking running containers</h3>
<pre><code class="lang-bash">docker ps
</code></pre>
<h3 id="heading-checking-stopped-containers-or-container-id-or-name">Checking stopped containers or container ID or name</h3>
<pre><code class="lang-bash">docker ps -a
</code></pre>
<h3 id="heading-checking-logs-using-container-name-or-id">Checking Logs using Container Name or ID</h3>
<pre><code class="lang-bash">docker logs &lt;container_name&gt;
</code></pre>
<h3 id="heading-stopping-a-docker-container">Stopping a Docker Container</h3>
<pre><code class="lang-bash">docker stop &lt;container_name&gt;
</code></pre>
<h3 id="heading-deleting-a-docker-container">Deleting a Docker Container</h3>
<pre><code class="lang-bash">docker rm &lt;container_name&gt;
</code></pre>
<h3 id="heading-remove-unnecessary-space-occupied-by-the-container-cache-after-deleting-the-docker-container">Remove Unnecessary Space occupied by the container (Cache) after deleting the Docker Container</h3>
<pre><code class="lang-bash">docker system prune -a
docker builder prune --all
</code></pre>
<h3 id="heading-list-docker-images">List Docker Images</h3>
<pre><code class="lang-bash">docker images
</code></pre>
<h3 id="heading-delete-a-docker-image">Delete a Docker Image</h3>
<pre><code class="lang-bash">docker rmi &lt;image_name&gt;
</code></pre>
<h3 id="heading-remove-all-unused-images">Remove all unused images</h3>
<pre><code class="lang-bash">docker image prune
</code></pre>
<p>And that is it, you can now deploy your application which contains the DockerFile and your application code to any cloud provider.</p>
<p>Also, if you need more commands, feel free to refer to the official docker documentation <a target="_blank" href="https://docs.docker.com/">here</a>, or the CLI CheatSheet linked <a target="_blank" href="https://docs.docker.com/get-started/docker_cheatsheet.pdf">here</a>.</p>
<p>Happy Deployment!</p>
<p>Until next time,</p>
<p>Shreyas</p>
]]></description><link>https://hashnode.shreyastaware.me/blog/docker-commands-to-make-your-life-a-breeze</link><guid isPermaLink="true">https://hashnode.shreyastaware.me/blog/docker-commands-to-make-your-life-a-breeze</guid><category><![CDATA[Docker]]></category><category><![CDATA[containers]]></category><category><![CDATA[cheatsheet]]></category><dc:creator><![CDATA[Shreyas Taware]]></dc:creator></item><item><title><![CDATA[How to use “pyenv” to manage your python versions and virtual environments?]]></title><description><![CDATA[<h1 id="heading-pre-requisites">Pre-requisites</h1>
<p>There are couple of things you should know before reading this blog:</p>
<ul>
<li>Basics of the <a target="_blank" href="https://docs.python.org/3/tutorial/">Python Programming Language</a></li>
<li>What is a Virtual Environment in Python?</li>
</ul>
<p>With these prequisites being fulfilled, let's dive straight into the blog. </p>
<h1 id="heading-motivation">Motivation</h1>
<p>Having multiple Python projects is a common occurence as a programmer.</p>
<p>Each project would have their respective python version and their compatible dependencies.</p>
<p>Thus, it is necessary to isolate these python versions and their dependencies from the global python installations and its dependencies so that we reduce version conflicts and have better control over our code.</p>
<h1 id="heading-what-is-pyenv">What is pyenv?</h1>
<p>Pyenv is a tool designed to manage multiple Python versions  whether they are local installations ie virtual environments or global installations  and their dependencies without interfering with each other. </p>
<h1 id="heading-how-pyenv-works">How Pyenv works:</h1>
<p>Pyenv operates by using "shims," which are small executable scripts that intercept Python commands. When you execute a python command, Pyenv's shims redirect the command to the currently selected Python version, ensuring that the correct interpreter is used.</p>
<h1 id="heading-how-to-install-pyenv">How to install pyenv?</h1>
<p>If you are using macOS, you can simply install it using:</p>
<pre><code>brew install pyenv
</code></pre><p>For operating system specific installation, feel free to follow the guide mentioned <a target="_blank" href="https://github.com/pyenv/pyenv">here</a>.</p>
<h1 id="heading-how-to-manage-pyenv">How to manage pyenv?</h1>
<p>Below are the essential commands every developer would need to manage python installations and different virtual environment.</p>
<h2 id="heading-frequently-used-commands">Frequently Used Commands</h2>
<h3 id="heading-first-find-your-username-using">First, find your username using:</h3>
<pre><code>whoami
</code></pre><h3 id="heading-to-check-the-list-of-python-versions-installed-using-pyenv">To check the list of python versions installed using pyenv</h3>
<pre><code>pyenv versions
</code></pre><h3 id="heading-to-check-the-global-list-of-python-versions-installed">To check the global list of python versions installed</h3>
<pre><code>ls /Users/<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">username</span>&gt;</span>/.pyenv/*</span>
</code></pre><p>You can see the "shims" or the aliases which are being used by pyenv, but they refer already installed python versions inside the operating system.</p>
<p>Now, we move towards installation of global and local python versions.</p>
<h3 id="heading-to-install-a-python-globally-using-pyenv">To install a python globally using pyenv</h3>
<pre><code>pyenv install <span class="hljs-number">3.13</span><span class="hljs-number">.0</span>
</code></pre><h3 id="heading-set-a-global-python-environment">Set a global python environment</h3>
<pre><code>pyenv <span class="hljs-built_in">global</span> <span class="hljs-number">3.13</span><span class="hljs-number">.0</span>
</code></pre><p><strong>Please Note:</strong> Setting a Global python version works only once you are outside of the local directory that has a pyenv virtual environment (explained further). So, feel free to set a global python anywhere outside of the local projects' directory, and you won't face any errors.</p>
<h3 id="heading-to-install-a-local-environment-use">To install a local environment, use</h3>
<pre><code>pyenv virtualenv &lt;python_version_no&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">env_name</span>&gt;</span></span>
</code></pre><p>for example:</p>
<pre><code>pyenv virtualenv <span class="hljs-number">3.11</span><span class="hljs-number">.8</span> aifc
</code></pre><p>Now, there are two ways using which you can activate a virtual environment: manually, and automatically (that is, when you change directory or <code>cd</code> into it).</p>
<h3 id="heading-for-manual-virtual-environment-activation">For manual virtual environment activation:</h3>
<pre><code>pyenv activate aifc
</code></pre><p>This commands activates the virtual environment as and when we invoke it.</p>
<h3 id="heading-for-directory-based-activation">For Directory Based Activation:</h3>
<pre><code>pyenv local aifc
</code></pre><p><strong>Pros (&amp; when to use it?):</strong></p>
<ul>
<li>This command activates the virtual environment.</li>
<li>Along with that it also sets the current directory it is in, as the local directory of that environment. So, whenever you change directory to that folder, this environment gets activated by default by pyenv.</li>
</ul>
<p><strong>When to use it?:</strong></p>
<ul>
<li>This command is best for recurring projects and projects which require collaboration.</li>
</ul>
<p><strong>Cons (&amp; how to mitigate it):</strong></p>
<ul>
<li>Entering this command also saves "aifc"  the local environment name instead of the actual python version used by that virtual environment in your ".python-version" file. </li>
</ul>
<p><img src="https://ik.imagekit.io/2vq13rcfz/s1.png?updatedAt=1761294046992" /></p>
<p>This is not ideal, since once we push this code, we or our peers won't be able to identify which python version needs to be installed for this project. </p>
<p>What we ideally want is  say instead of "aifc" it was "3.10.13" the python version that was saved. Let's learn how to do that:</p>
<p><strong>How to mitigate this?</strong></p>
<p>1) Add the ".python-version" file name to your ".gitignore" file.</p>

<p><img src="https://ik.imagekit.io/2vq13rcfz/s3.png?updatedAt=1761294277216" /></p>
<p>2) Once you do that, save the python version number of the local environment by creating the ".python-version.example" file and saving inside it.</p>

<p><img src="https://ik.imagekit.io/2vq13rcfz/s2.png?updatedAt=1761294047233" /></p>
<p>This way the exact version number of the project is saved into the ".python-version.example" file, while the file that sets your environment ".python-version" gets ignored by version control (since you put it inside .gitignore file). </p>
<p>This lets you or your peers know about the python version for that particular project, while you get the benefits of locally changing directory without having to activate or deactivate the respective virtual environment!</p>
<p><a href="https://imgflip.com/i/aa04qc"><img src="https://i.imgflip.com/aa04qc.jpg" /></a></p>
<p>Till now, we have learnt how to install pyenv, lookup different python versions, install, and activate local and global pyenv python versions.</p>
<p>Now let us learn how to deactivate the virtual environment:</p>
<p>Once we activate the virtual environment, the directory is ready to be used for the project. </p>
<h3 id="heading-to-deactivate-the-virtual-environment-for-directory-based-activation">To deactivate the virtual environment (for Directory based activation):</h3>
<pre><code>pyenv local --unset
</code></pre><h3 id="heading-to-deactivate-the-virtual-environment-for-manual-activation">To deactivate the virtual environment (for manual activation):</h3>
<pre><code>pyenv deactivate
</code></pre><h3 id="heading-how-to-check-the-currently-active-pyenv-virtual-environment">How to check the currently active pyenv virtual environment</h3>
<pre><code>pyenv version
</code></pre><h3 id="heading-to-check-python-version-of-the-currently-active-pyenv-virtual-environment">To check python version of the currently active pyenv virtual environment</h3>
<pre><code>python --version
</code></pre><p>Finally, we may need to delete the global python version or the virtual environment we created. Here's how to do that:</p>
<h3 id="heading-deleting-the-global-python-version">Deleting the global python version</h3>
<pre><code>pyenv uninstall &lt;version&gt;
</code></pre><h3 id="heading-deleting-the-virtual-environment">Deleting the virtual environment</h3>
<pre><code>pyenv virtualenv-<span class="hljs-keyword">delete</span> &lt;env-name&gt;
</code></pre><p>And that is it!</p>
<p>If you came till the end, thank you so much! It means a lot to me!</p>
<p></p><blockquote><p>"Every strike brings me closer to the next home run." -- Babe Ruth</p> Inspired. Motivated. Ready to Go🚀 (@Inspire_To_Win) <a href="https://twitter.com/Inspire_To_Win/status/1743554133855236181?ref_src=twsrc%5Etfw">January 6, 2024</a></blockquote> <p></p>
<p>Cheers.</p>
<p>Until next time,</p>
<p>Shreyas</p>
]]></description><link>https://hashnode.shreyastaware.me/blog/how-to-use-pyenv-to-manage-your-python-versions-and-virtual-environments</link><guid isPermaLink="true">https://hashnode.shreyastaware.me/blog/how-to-use-pyenv-to-manage-your-python-versions-and-virtual-environments</guid><category><![CDATA[pyenv]]></category><category><![CDATA[Python]]></category><category><![CDATA[virtual environment]]></category><category><![CDATA[python beginner]]></category><dc:creator><![CDATA[Shreyas Taware]]></dc:creator></item><item><title><![CDATA[How to get started with Git and Github?]]></title><description><![CDATA[<p>Welcome to the first blog on the <strong>#GetStartedWithGit</strong> Series. In this blog, you will get to know the working of Git and how to setup Git for the first time.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637576578482/QloO2o32nw.png" alt="image.png" /></p>
<p>Use Case - A web developer or a graphic designer needed to record various versions of a specific file so that they could compare different versions and select the best one. </p>
<p>Originally, the straightforward way was to just create another copy of the file make changes to it, and then use it. Like this:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637574013544/xaCtfzhn-.png" alt="image.png" /></p>
<p>But as it was too clumsy to handle, there was a need to create a system that would handle this, without the need to create a new copy of the file.</p>
<p>TADA! Here comes Version Control</p>
<h2 id="heading-what-is-version-control">What is Version Control?</h2>
<p>Version Control is a system that records modifications to a file or a repository (folder of files) over time so that you can recall specific versions of that later. </p>
<h2 id="heading-what-is-git">What is Git?</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637576430118/DqRVr8NCi.png" alt="image.png" /></p>
<p>Git is a distributed version control system (DVCS) whose goal is to track and manage file changes and collaborate with multiple developers working on the same project.</p>
<p>A distributed version control system decentralizes the data, and every client computer not only records the snapshot of the latest files but fully mirrors the repository.</p>
<p>You might also have come across the term - Github. But, its not the same as Git.</p>
<h2 id="heading-so-what-is-github">So, What is Github?</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637576418749/rcMDSYW3y.png" alt="image.png" /></p>
<p>Github and similar services like Gitlab or BitBucket are websites that hold a Git server program to hold your code.</p>
<h2 id="heading-git-basics">Git Basics</h2>
<p>Git stores snapshots of files at every interval and not differences. So, if a file has not changed in some interval, it reuses the same file instead of creating another copy. </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637574605803/atgXDNxlr.png" alt="image.png" /></p>
<h2 id="heading-the-3-main-states">The 3 main states</h2>
<ol>
<li><p>Modified - It means that you made changes to the file, but did not stage those changes.</p>
</li>
<li><p>Staged - It is an area where snapshots of files are added temporarily so that they are ready to be committed/ saved permanently.</p>
</li>
<li><p>Committed - It means taking a permanent snapshot of the files that are staged. It also means saving the files in your Git Version Control System.</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1637575297230/b4NCiy-Rr.png" alt="image.png" /></p>
<p>But, we also need to know few things - </p>
<p><strong>Local/ Working Directory</strong> - It is the directory that you work with on your local computer. These files are pulled from the database in the Git Directory and placed on the disk on your computer for you to modify or this directory is directly synced with another newly created repository on Github and thus works as a Git Repository.</p>
<p><strong>Git Directory</strong> - It is where Git stores the metadata and object database for your project.</p>
<p><strong> Staging area/ Index </strong> - This is a file that stores information about what will go into your next commit.</p>
<h2 id="heading-basic-git-workflow">Basic Git Workflow</h2>
<ol>
<li><p>You modify files in your working directory.</p>
</li>
<li><p>You stage the files, adding snapshots of them to your staging area.</p>
</li>
<li><p>You do a commit, which takes the files as they are in the staging area and stores that snapshot permanently to your Git directory.</p>
</li>
</ol>
<h2 id="heading-first-time-git-setup">First Time Git Setup</h2>
<p>If you are on ubuntu, the process is pretty straightforward. Just open your terminal and type - </p>
<pre><code>sudo apt-<span class="hljs-keyword">get</span> install git-<span class="hljs-keyword">all</span>
</code></pre><p>To get a list of settings you can type</p>
<pre><code>git config <span class="hljs-comment">--list</span>
</code></pre><p>to check your preferred editor you can type</p>
<pre><code><span class="hljs-selector-tag">git</span> <span class="hljs-selector-tag">config</span> <span class="hljs-selector-tag">core</span><span class="hljs-selector-class">.editor</span>
</code></pre><p>To set the username, email address and the editor (I would highly encourage using <em>vim</em> as it is great, though <em>nano</em> will work fine too)</p>
<pre><code>git config --<span class="hljs-built_in">global</span> user.name <span class="hljs-string">"Shreyas Taware"</span>
git config --<span class="hljs-built_in">global</span> user.email shreyastaware13@gmail.com
git config --<span class="hljs-built_in">global</span> core.editor vim
</code></pre><p>Once this is done, we are good to go. But wait, we need to figure out a way if we are stuck.</p>
<h2 id="heading-getting-help-using-git">Getting Help using Git</h2>
<pre><code>git help <span class="hljs-tag">&lt;<span class="hljs-name">verb</span>&gt;</span>
git <span class="hljs-tag">&lt;<span class="hljs-name">verb</span>&gt;</span> --help
man git-<span class="hljs-tag">&lt;<span class="hljs-name">verb</span>&gt;</span>
</code></pre><p>Try this:</p>
<pre><code>git <span class="hljs-built_in">help</span> config
</code></pre><p>Part 2 of the series will be coming shortly.</p>
]]></description><link>https://hashnode.shreyastaware.me/blog/how-to-get-started-with-git-and-github</link><guid isPermaLink="true">https://hashnode.shreyastaware.me/blog/how-to-get-started-with-git-and-github</guid><category><![CDATA[GitHub]]></category><category><![CDATA[Git]]></category><category><![CDATA[version control]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Shreyas Taware]]></dc:creator></item></channel></rss>