So, Why Do You Like Python?

“So, Why Do You Like Python?” — An Interview Reflection

How a simple interview question led me to deeply reflect on programming language philosophy


The Interview Moment

There I was, sitting in a virtual interview for a Software Engineer position. We’d covered system design, talked through my experience scaling microservices, and discussed architectural trade-offs. Then came a question I wasn’t expecting:

“You’ve worked with many languages throughout your career. Why do you like Python, and what makes it different from the others?”

I paused. Not because I didn’t have an answer. I’ve been writing Python for over six years, but because I realized I’d never truly articulated it before. It’s one thing to use a tool every day; it’s another to explain why that tool feels like an extension of your thinking.

Here’s what I shared, expanded into the reflection I wish I’d given.


Why I Love Python: Beyond the Syntax

1. Readability Is Not Just a Feature. It’s a Philosophy

Python’s creator, Guido van Rossum, designed the language with a radical idea: code is read more often than it is written. This principle, enshrined in PEP 20 (The Zen of Python), shapes everything about the language.

# The Zen of Python (try running: import this)
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Readability counts.

When I review a Python codebase, I can often understand the intent without extensive documentation. Compare this simple example across languages:

Python:

def get_active_users(users):
    return [user for user in users if user.is_active]

Java:

public List<User> getActiveUsers(List<User> users) {
    return users.stream()
                .filter(User::isActive)
                .collect(Collectors.toList());
}

C++:

std::vector<User> getActiveUsers(const std::vector<User>& users) {
    std::vector<User> result;
    std::copy_if(users.begin(), users.end(), 
                 std::back_inserter(result),
                 [](const User& u) { return u.isActive(); });
    return result;
}

The Python version reads almost like English. This isn’t just aesthetic—it reduces cognitive load during code reviews, debugging, and onboarding new team members.

2. The “Batteries Included” Standard Library

Python ships with an incredibly comprehensive standard library. Need to work with JSON? import json. HTTP requests? urllib is built-in. Regular expressions, date handling, file operations, concurrent programming—it’s all there.

# No external dependencies needed for common tasks
import json
import datetime
import pathlib
import concurrent.futures
import urllib.request

# Parse JSON, handle dates, manage files - all built-in
config = json.loads(pathlib.Path("config.json").read_text())

3. The Ecosystem Is Unmatched for Certain Domains

┌─────────────────────────────────────────────────────────────────┐
│                  Python's Ecosystem Dominance                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  Data Science & ML          Web Development                     │
│  ─────────────────          ───────────────                     │
│  • NumPy                    • Django                            │
│  • Pandas                   • FastAPI                           │
│  • Scikit-learn             • Flask                             │
│  • TensorFlow               • Celery                            │
│  • PyTorch                  • SQLAlchemy                        │
│                                                                 │
│  DevOps & Automation        Scientific Computing                │
│  ──────────────────         ────────────────────                │
│  • Ansible                  • SciPy                             │
│  • Fabric                   • Matplotlib                        │
│  • Boto3 (AWS)              • Jupyter                           │
│  • Docker SDK               • SymPy                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

How Python Differs: A Technical Deep Dive

When the interviewer asked what sets Python apart, I knew I had to go beyond surface-level observations. Here’s the technical breakdown:

Type System Comparison

┌────────────────────────────────────────────────────────────────────────────┐
                         Type System Spectrum                               
                                                                            
   Static                                              Dynamic              
   Strongly Typed                                      Weakly Typed         
                                                                          
                                                                          
   ┌───────┐  ┌──────┐  ┌────────┐  ┌────────┐  ┌──────────────┐           
    Rust     Java      Go      Python     JavaScript             
                                                                 
   Static   Static   Static   Dynamic      Dynamic               
   Strong   Strong   Strong    Strong       Weak                 
   └───────┘  └──────┘  └────────┘  └────────┘  └──────────────┘           
                                                                            
   Compile-time        ◄─────────────────────►      Runtime                 
   type checking                                    type checking           
└────────────────────────────────────────────────────────────────────────────┘

Python is dynamically typed but strongly typed. This is a crucial distinction:

# Dynamic typing: types are checked at runtime
x = 10        # x is an int
x = "hello"   # now x is a str - perfectly valid

# Strong typing: no implicit type coercion
result = "10" + 5   # TypeError! Python won't silently convert

Compare to JavaScript (dynamic + weak typing):

// JavaScript allows this - weak typing
const result = "10" + 5;  // "105" - string concatenation!

Memory Management Approaches

┌─────────────────────────────────────────────────────────────────────────┐
                    Memory Management Strategies                         
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐     
       Manual           Garbage                Ownership          
     Management         Collection              System            
  ├─────────────────┤    ├─────────────────┤    ├─────────────────┤     
                                                                  
    C, C++               Python               Rust                
                         Java                                     
    malloc/free          Go                   Borrow checker      
    new/delete           JavaScript           Zero-cost           
                                              abstractions        
    ⚠️  Risk of           Automatic           No GC pause       
    memory leaks         ⚠️ GC pauses         ⚠️ Steep            
    and dangling           possible             learning          
    pointers                                    curve             
  └─────────────────┘    └─────────────────┘    └─────────────────┘     
                                                                        
  Developer Control ◄──────────────────────────► Safety & Convenience   
└────────────────────────────────────────────────────────────────────────┘

Python uses reference counting combined with a cyclic garbage collector:

import sys

# Reference counting in action
a = [1, 2, 3]
print(sys.getrefcount(a))  # 2 (one for 'a', one for the getrefcount argument)

b = a  # Another reference
print(sys.getrefcount(a))  # 3

del b  # Reference removed
print(sys.getrefcount(a))  # Back to 2

Execution Model Comparison

┌────────────────────────────────────────────────────────────────────────┐
                        Execution Model Flow                            
└────────────────────────────────────────────────────────────────────────┘

  COMPILED LANGUAGES (C, C++, Rust, Go)
  ─────────────────────────────────────

  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
   Source Code  │───►│   Compiler   │───►│   Machine    │───► Execution
     (.c/.rs)                             Code      
  └──────────────┘    └──────────────┘    └──────────────┘


  JIT COMPILED (Java, C#)
  ───────────────────────

  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────┐
   Source Code  │───►│   Compiler   │───►│  Bytecode    │───►│   JIT    │───►
     (.java)                            (.class)         Compiler 
  └──────────────┘    └──────────────┘    └──────────────┘    └──────────┘
                                                                    
                                                                    
                                                              ┌──────────┐
                                                               Machine  
                                                                Code    
                                                              └──────────┘


  INTERPRETED (Python - CPython)
  ──────────────────────────────

  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
   Source Code  │───►│  Bytecode    │───►│   Python     │───► Execution
      (.py)           Compiler         Virtual      
                                       Machine      
  └──────────────┘      (.pyc)           (Interpreter)
                      └──────────────┘    └──────────────┘

Performance Characteristics

Let’s be honest. Python isn’t the fastest language. But context matters:

┌─────────────────────────────────────────────────────────────────────────┐
              Relative Performance (Lower is Faster)                     
                    Benchmark: Fibonacci(40)                             
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  C          ████ 0.15s                                                  
                                                                         
  Rust       █████ 0.16s                                                 
                                                                         
  Go         ███████ 0.28s                                               
                                                                         
  Java       ████████ 0.32s                                              
                                                                         
  JavaScript ██████████████ 0.58s (V8)                                   
                                                                         
  Python     ████████████████████████████████████████████████ 22.5s      
                                                                         
  PyPy       ███████████ 0.45s (JIT-compiled Python)                     
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

  Note: These are illustrative benchmarks. Real-world performance depends
  heavily on the specific workload and optimization techniques used.

But here’s what I told the interviewer: Raw computation speed rarely matters in real-world applications. Most backend services are I/O bound—waiting on databases, APIs, and file systems. Python’s async capabilities handle this beautifully:

import asyncio
import aiohttp

async def fetch_all_users(user_ids: list[int]) -> list[dict]:
    """Fetch multiple users concurrently - I/O bound operation"""
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_user(session, uid) for uid in user_ids]
        return await asyncio.gather(*tasks)

async def fetch_user(session: aiohttp.ClientSession, user_id: int) -> dict:
    async with session.get(f"https://api.example.com/users/{user_id}") as resp:
        return await resp.json()

The Global Interpreter Lock (GIL)

I can’t discuss Python’s differences without mentioning the GIL—the most misunderstood aspect of CPython:

┌─────────────────────────────────────────────────────────────────────────┐
│                    Understanding the GIL                                │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  What the GIL Does:                                                     │
│  ──────────────────                                                     │
│  Only ONE thread can execute Python bytecode at a time                  │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│                          Time ───────►                               │
│                                                                      │
│    Thread 1: ████████░░░░░░░░████████░░░░░░░░████████                │
│                                                                  │
│    Thread 2: ░░░░░░░░████████░░░░░░░░████████░░░░░░░░                │
│                                                                  │
│    GIL:      ────────┴───────┴───────┴───────┴──────                 │
│              (threads take turns holding the lock)                   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                                                         │
│  When GIL Matters:              When GIL Doesn't Matter:               │
│  ───────────────────            ─────────────────────────              │
│  • CPU-bound tasks              • I/O-bound tasks                      │
│  • Pure Python number           • Database queries                     │
│    crunching                    • Network requests                     │
│  • Image processing             • File operations                      │
│    (without NumPy)              • Web servers                          │
│                                 • NumPy operations (releases GIL)      │
│                                                                         │
│  Solutions for CPU-bound:                                              │
│  ────────────────────────                                              │
│  • multiprocessing module (separate processes, no shared GIL)          │
│  • Cython or Numba (compile to native code)                            │
│  • PyPy (JIT compilation)                                              │
│  • Python 3.13+ free-threaded builds (experimental)                    │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
# CPU-bound: Use multiprocessing to bypass GIL
from multiprocessing import Pool

def cpu_intensive_task(n):
    return sum(i * i for i in range(n))

if __name__ == "__main__":
    with Pool(processes=4) as pool:
        results = pool.map(cpu_intensive_task, [10_000_000] * 4)

Modern Python: Type Hints and Beyond

One thing I emphasized in the interview: Python has evolved dramatically. Modern Python (3.10+) with type hints feels like a different language:

from dataclasses import dataclass
from typing import Optional
from datetime import datetime

@dataclass
class User:
    id: int
    email: str
    created_at: datetime
    last_login: Optional[datetime] = None

    def is_recently_active(self, days: int = 30) -> bool:
        if self.last_login is None:
            return False
        delta = datetime.now() - self.last_login
        return delta.days <= days


def process_users(users: list[User]) -> dict[str, list[User]]:
    """Categorize users by activity status."""
    return {
        "active": [u for u in users if u.is_recently_active()],
        "inactive": [u for u in users if not u.is_recently_active()]
    }

Combined with tools like mypy, Pydantic, and FastAPI, you get the best of both worlds: Python’s flexibility during development with static analysis catching bugs before production.


When NOT to Use Python

Being a Staff Engineer means knowing when not to use your favorite tool. I told the interviewer:

┌─────────────────────────────────────────────────────────────────────────┐
                    Language Selection Guide                             
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  Use Case                          Recommended Language(s)              
  ─────────────────────────────     ─────────────────────────────────    
                                                                         
  Systems programming               Rust, C, C++                         
  (OS, drivers, embedded)                                                
                                                                         
  High-frequency trading            C++, Rust                            
  (nanosecond latency)                                                   
                                                                         
  Mobile applications               Swift (iOS), Kotlin (Android)        
                                                                         
  Browser/Frontend                  JavaScript/TypeScript                
                                                                         
  Game engines                      C++, C#                              │
                                                                         
  ──────────────────────────────────────────────────────────────────     
                                                                         
  PYTHON EXCELS AT:                                                      
                                                                         
   Backend services & APIs          DevOps & automation               
   Data science & ML                Scientific computing              
   Rapid prototyping                Scripting & tooling               
   ETL pipelines                    Testing frameworks                
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

The Interview Outcome

After my response, the interviewer smiled and said something I’ll remember: “You clearly don’t just use Python—you understand it.”

That’s the difference between a developer and an engineer. Tools are just tools. What matters is understanding:
- Why a tool was designed the way it was
- When to use it versus alternatives
- How to push its limits when needed

Python’s greatest strength isn’t any single feature—it’s the philosophy behind it. A language that prioritizes human readability, developer happiness, and “one obvious way to do things” creates codebases that teams can maintain for years.


Key Takeaways

┌─────────────────────────────────────────────────────────────────────────┐
                         Summary: Python's Identity                      │
├─────────────────────────────────────────────────────────────────────────┤
                                                                         
  ┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐         
     Philosophy         Practicality        Evolution             
  ├─────────────────┤  ├─────────────────┤  ├─────────────────┤         
    Readability       Batteries         Type hints             
     first              included          Async/await            
    Explicit >        Massive           Pattern                
     implicit           ecosystem          matching               
    One obvious       Quick             Performance            
     way                prototyping        improvements           
  └─────────────────┘  └─────────────────┘  └─────────────────┘         
                                                                         
  The best tool is the one your team can read, maintain, and ship with. 
                                                                         
└─────────────────────────────────────────────────────────────────────────┘

References

  1. Van Rossum, G., & Drake, F. L. (2023). The Python Language Reference. Python Software Foundation. https://docs.python.org/3/reference/

  2. PEP 20 – The Zen of Python. Python Enhancement Proposals. https://peps.python.org/pep-0020/

  3. PEP 484 – Type Hints. Python Enhancement Proposals. https://peps.python.org/pep-0484/

  4. Beazley, D. M. (2010). Understanding the Python GIL. PyCon 2010. https://www.dabeaz.com/python/UnderstandingGIL.pdf

  5. The Benchmarks Game. Programming Language Benchmarks. https://benchmarksgame-team.pages.debian.net/benchmarksgame/

  6. Python Developer Survey 2023 Results. JetBrains & Python Software Foundation. https://lp.jetbrains.com/python-developers-survey-2023/

  7. Lutz, M. (2013). Learning Python (5th ed.). O’Reilly Media.

  8. Ramalho, L. (2022). Fluent Python (2nd ed.). O’Reilly Media.

  9. PEP 703 – Making the Global Interpreter Lock Optional. Python Enhancement Proposals. https://peps.python.org/pep-0703/

  10. Python 3.13 Release Notes – Free-threaded CPython. Python Software Foundation. https://docs.python.org/3.13/whatsnew/3.13.html


Written from the perspective of Software Engineer reflecting on what makes Python unique—not just as a language, but as a way of thinking about code.

Last updated: December 2025

Discussion (0)

Share your thoughts and join the conversation

?
0/10000
Loading comments...