Introduction to Python #
Some languages were born to be fast, some were born to be safe, and then there’s Python — born to be readable by humans. Guido van Rossum started Python in the late 1980s not because the world lacked programming languages, but because he was disappointed with ABC — a language whose philosophy he loved but whose implementation was too closed for development. Python took ABC’s philosophy (readability, simplicity, no unnecessary boilerplate) but made it open source, extensible, and pragmatic. The result is a language that in three decades managed to dominate very different domains simultaneously: web development, data science, machine learning, scripting, education, and scientific research. No other language has achieved the same breadth. This article discusses why Python was designed this way, how Python evolved from version 1 to 3.12, what makes its ecosystem unique, and when Python is the right choice — and when it isn’t.
The Philosophy of Python — The Zen of Python #
Every programming language has implicit design decisions. Python has something more explicit: The Zen of Python, written by Tim Peters, available directly from the Python interpreter with import this. This isn’t formal documentation — it’s a guiding principle that influences every language design decision and how the Python community writes code.
import this
# Output (19 aphorisms):
# Beautiful is better than ugly.
# Explicit is better than implicit.
# Simple is better than complex.
# Complex is better than complicated.
# Flat is better than nested.
# Sparse is better than dense.
# Readability counts.
# Special cases aren't special enough to break the rules.
# Although practicality beats purity.
# Errors should never pass silently.
# Unless explicitly silenced.
# In the face of ambiguity, refuse the temptation to guess.
# There should be one-- and preferably only one --obvious way to do it.
# Now is better than never.
# Although never is often better than *right* now.
# If the implementation is hard to explain, it's a bad idea.
# If the implementation is easy to explain, it may be a good idea.
# Namespaces are one honking great idea -- let's do more of those!
The three principles that most impact how Python is written daily:
Readability counts — Python uses indentation as part of the syntax, not a style choice. This forces all Python code to look consistent and be easy to read. No curly braces making developers argue over where they go.
Explicit is better than implicit — Python avoids hidden “magic”. If something happens, you can see it in the code. This differs from languages like Ruby that love hidden metaprogramming.
There should be one obvious way to do it — Unlike Perl or Ruby, which pride themselves on TIMTOWTDI (There Is More Than One Way To Do It), Python prefers one clear way over many confusing ones. gofmt in Go was inspired by this philosophy.
flowchart TD
A[The Zen of Python] --> B[Readability]
A --> C[Explicitness]
A --> D[Simplicity]
A --> E[Practicality]
B --> B1[Indentation as syntax]
B --> B2[Descriptive variable names]
C --> C1[Explicit imports]
C --> C2[No hidden magic methods]
D --> D1[One obvious way for every task]
D --> D2[Flat is better than nested]
E --> E1[Practicality beats purity]
E --> E2[Batteries included stdlib]History and Evolution of Python #
Python isn’t a language born from a large corporate project — it was born from one person with a clear vision of what a pleasant-to-use language should look like.
| Year | Version | Important Milestone |
|---|---|---|
| 1989 | — | Guido van Rossum starts working on Python at CWI, the Netherlands |
| 1991 | 0.9.0 | First public release: functions, exceptions, str/list/dict already present |
| 1994 | 1.0 | First stable release: lambda, map, filter, reduce |
| 2000 | 2.0 | List comprehensions, garbage collection, Unicode support |
| 2008 | 3.0 | Major breaking change: print became a function, str/bytes separated, consistent division |
| 2010 | 2.7 | Last 2.x version, many Python 3 features backported |
| 2015 | 3.5 | async/await — native asynchronous programming |
| 2016 | 3.6 | f-strings, variable type annotations, ordered dicts by default |
| 2018 | 3.7 | Data classes, breakpoint(), context variables |
| 2019 | 3.8 | Walrus operator :=, f"{val!r}" debug format |
| 2020 | 2 EOL | Python 2 officially End of Life — no more security updates |
| 2020 | 3.9 | dict | dict merge, list[int] as direct type hints |
| 2021 | 3.10 | Structural pattern matching (match/case), better error messages |
| 2022 | 3.11 | 40–60% faster than 3.10 (CPython Faster project) |
| 2023 | 3.12 | Per-interpreter GIL, more flexible f-strings, pathlib improvements |
| 2024 | 3.13 | Experimental free-threaded mode (no GIL), experimental JIT compiler |
The biggest leap in Python’s modern history is Python 3.11. The CPython team, led by Łukasz Langa and Mark Shannon, launched the “Faster CPython” project funded by Microsoft to make CPython significantly faster. The result: Python 3.11 is 40–60% faster than Python 3.10 for many workloads. Python 3.13 brings something more radical: an experimental free-threaded mode that disables the GIL — something the Python community dreamed about for nearly 30 years.
stateDiagram-v2
[*] --> PythonEarly: 1991-1999
PythonEarly --> Python2Era: Python 2.0 (2000)
Python2Era --> Python3Era: Python 3.0 (2008)
Python3Era --> AsyncEra: async/await 3.5 (2015)
AsyncEra --> ModernEra: 3.10+ (2021)
ModernEra --> [*]
PythonEarly: Simple scripting language, small community
Python2Era: Mass adoption, web frameworks, Unicode
Python3Era: Breaking changes, 12-year-long transition
AsyncEra: FastAPI, data science boom, ML dominance
ModernEra: Faster CPython, pattern matching, free-threaded GILModern Python Features #
Type Hints and Static Analysis #
Python is a dynamic language — types aren’t checked at runtime unless you ask. But since Python 3.5, Python supports type hints: type annotations used by tools like mypy, pyright, or basedpyright for static analysis, without changing runtime behavior.
# ANTI-PATTERN: function without type hints — ambiguous for readers and tools
def proses_data(data, threshold):
hasil = []
for item in data:
if item > threshold:
hasil.append(item * 2)
return hasil
# CORRECT: type hints clarify the function contract
def proses_data(data: list[float], threshold: float) -> list[float]:
return [item * 2 for item in data if item > threshold]
# More complex types
from typing import Optional, Union, TypeVar, Generic
def cari_pengguna(user_id: int) -> Optional[dict[str, str]]:
# Optional[X] = X | None
if user_id <= 0:
return None
return {"id": str(user_id), "nama": f"Pengguna {user_id}"}
# Python 3.10+ — union types with | directly
def proses(nilai: int | float | None) -> str:
match nilai:
case None:
return "kosong"
case int(n) if n < 0:
return f"negatif: {n}"
case int(n) | float(n):
return f"positif: {n}"
Comprehensions — Declarative Syntax #
Comprehensions are one of the most Pythonic features — an expressive way to create collections from iteration.
angka = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# ANTI-PATTERN: imperative loop for a simple transformation
genap = []
for n in angka:
if n % 2 == 0:
genap.append(n * n)
# CORRECT: list comprehension — declarative, one line
genap = [n * n for n in angka if n % 2 == 0]
# => [4, 16, 36, 64, 100]
# Dict comprehension
kuadrat_map = {n: n**2 for n in range(1, 6)}
# => {1: 1, 2: 4, 3: 9, 4: 16, 5: 25}
# Set comprehension — automatically deduplicates
huruf_unik = {c.lower() for c in "Python Programming" if c.isalpha()}
# Generator expression — lazy evaluation, memory efficient
total = sum(n**2 for n in range(1_000_000)) # doesn't build a 1-million-item list
Decorators #
A decorator is a pattern for modifying the behavior of functions or classes without changing their original code. It’s Python’s implementation of Higher-Order Functions, widely used in web frameworks, testing, and caching.
import functools
import time
# Simple decorator — measuring execution time
def ukur_waktu(fungsi):
@functools.wraps(fungsi) # preserve the original function's metadata
def wrapper(*args, **kwargs):
mulai = time.perf_counter()
hasil = fungsi(*args, **kwargs)
selesai = time.perf_counter()
print(f"{fungsi.__name__} finished in {selesai - mulai:.4f} seconds")
return hasil
return wrapper
@ukur_waktu
def hitung_berat(n: int) -> int:
return sum(i**2 for i in range(n))
hitung_berat(1_000_000)
# => hitung_berat finished in 0.1234 seconds
# Decorator with arguments
def retry(maks_coba: int = 3, delay: float = 1.0):
def decorator(fungsi):
@functools.wraps(fungsi)
def wrapper(*args, **kwargs):
for percobaan in range(maks_coba):
try:
return fungsi(*args, **kwargs)
except Exception as e:
if percobaan == maks_coba - 1:
raise
print(f"Attempt {percobaan + 1} failed: {e}. Trying again...")
time.sleep(delay)
return wrapper
return decorator
@retry(maks_coba=3, delay=0.5)
def panggil_api(url: str) -> dict:
# simulate a request that can fail
import random
if random.random() < 0.7:
raise ConnectionError("Connection failed")
return {"status": "ok"}
Async/Await and Asyncio #
Python 3.5 introduced async/await as a native way to write asynchronous code without callback hell.
import asyncio
import aiohttp
# Async function -- defined with async def
async def ambil_data(session: aiohttp.ClientSession, url: str) -> dict:
async with session.get(url) as response:
return await response.json()
# ANTI-PATTERN: sequential -- waiting one at a time (slow)
async def ambil_sequential():
async with aiohttp.ClientSession() as session:
data1 = await ambil_data(session, "https://api.example.com/users/1")
data2 = await ambil_data(session, "https://api.example.com/users/2")
data3 = await ambil_data(session, "https://api.example.com/users/3")
return [data1, data2, data3]
# CORRECT: concurrent -- run everything at once with asyncio.gather
async def ambil_concurrent():
urls = [
"https://api.example.com/users/1",
"https://api.example.com/users/2",
"https://api.example.com/users/3",
]
async with aiohttp.ClientSession() as session:
tasks = [ambil_data(session, url) for url in urls]
hasil = await asyncio.gather(*tasks)
return hasil
# Running the event loop
if __name__ == "__main__":
asyncio.run(ambil_concurrent())
Python async/await is only effective for I/O-bound tasks (network requests, file I/O, database queries). For CPU-bound tasks (heavy computation, image processing, ML inference),asynciodoesn’t help because the GIL still blocks true parallelism. For CPU-bound, usemultiprocessingorProcessPoolExecutor. Python 3.13 is starting to introduce an experimental free-threaded mode that disables the GIL.
Pattern Matching (Python 3.10+) #
match/case is structural pattern matching — far more powerful than switch/case in other languages because it can do destructuring.
from dataclasses import dataclass
@dataclass
class Titik:
x: float
y: float
@dataclass
class Lingkaran:
pusat: Titik
radius: float
@dataclass
class Persegi:
kiri_atas: Titik
kanan_bawah: Titik
def deskripsikan(bentuk) -> str:
match bentuk:
case Lingkaran(pusat=Titik(x=0, y=0), radius=r):
return f"Circle at the origin with radius {r}"
case Lingkaran(radius=r) if r > 100:
return f"Very large circle (radius {r})"
case Lingkaran(pusat=p, radius=r):
return f"Circle at ({p.x}, {p.y}), radius {r}"
case Persegi(kiri_atas=Titik(x=x1, y=y1), kanan_bawah=Titik(x=x2, y=y2)):
lebar = x2 - x1
tinggi = y2 - y1
return f"Square {lebar}x{tinggi}"
case _:
return "Unknown shape"
The Ecosystem: pip, virtualenv, and PyPI #
Python has a huge package ecosystem through PyPI (Python Package Index) with more than 500,000 packages available.
# Package management with pip
pip install requests # install a package
pip install "django>=4.2,<5.0" # specific version
pip install -r requirements.txt # install from a file
pip list # list installed packages
pip freeze > requirements.txt # export dependencies
pip show requests # detailed package info
# Virtual environments -- REQUIRED for every project
python -m venv venv # create a virtual environment
source venv/bin/activate # activate (Linux/macOS)
venv\Scripts\activate # activate (Windows)
deactivate # deactivate
# uv -- a modern package manager far faster than pip
pip install uv
uv init nama-project # init a new project
uv add requests fastapi # add dependencies
uv run python main.py # run within the environment
uv sync # sync dependencies from a lockfile
Example pyproject.toml for a modern project (PEP 517/518 standard):
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "aplikasi-saya"
version = "1.0.0"
requires-python = ">=3.11"
dependencies = [
"fastapi>=0.110",
"sqlalchemy>=2.0",
"pydantic>=2.0",
"httpx>=0.27",
"redis>=5.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"mypy>=1.9",
"ruff>=0.4", # modern linter + formatter, replacing flake8+black
"pytest-asyncio",
]
Key Libraries per Domain #
| Domain | Library | Purpose |
|---|---|---|
| Web Frameworks | FastAPI, Django, Flask | HTTP servers and APIs |
| HTTP Clients | httpx, requests, aiohttp | Consuming APIs |
| ORM | SQLAlchemy, Django ORM, Tortoise | Database access |
| Data Science | pandas, NumPy, Polars | Data manipulation |
| Visualization | Matplotlib, Plotly, Seaborn | Graphs and charts |
| Machine Learning | scikit-learn, XGBoost, LightGBM | Classical ML |
| Deep Learning | PyTorch, TensorFlow, JAX | Neural networks |
| NLP | Transformers (HuggingFace), spaCy | Text processing |
| Testing | pytest, unittest, hypothesis | Testing |
| Linting | ruff, mypy, pylint | Code quality |
| CLI | click, typer, argparse | Command-line apps |
| Task Queues | Celery, arq, dramatiq | Background jobs |
Python Implementation Variants #
CPython is the reference implementation of Python, but there are several other implementations relevant for specific use cases.
| Implementation | Written in | Main Advantage | Suitable for |
|---|---|---|---|
| CPython | C | Full compatibility, largest ecosystem | Almost every use case |
| PyPy | RPython | JIT compiler — 5–10x faster for long-running programs | Programs with lots of numerical computation |
| Jython | Java | Integration with the JVM ecosystem | Projects needing Java interop |
| IronPython | C# | Integration with .NET | .NET projects needing Python |
| MicroPython | C | Very lightweight, runs on microcontrollers | IoT, embedded systems |
| CircuitPython | C | MicroPython fork for education | Hobby electronics projects with Adafruit |
PyPy is often a source of confusion. PyPy is very fast for pure Python code that runs long (game servers, simulations, batch processing). But PyPy is not compatible with all C extension libraries — NumPy, pandas, and most data science libraries don’t run optimally on PyPy. For data science, stick with CPython. For workloads whose bottleneck is in pure Python loops, PyPy can be an attractive choice.
The GIL — Global Interpreter Lock #
The GIL (Global Interpreter Lock) is a mechanism in CPython ensuring only one thread executes Python bytecode at a time. It’s one of the most misunderstood topics about Python.
flowchart TD
A{Type of task?} --> B[I/O-bound\nNetwork, file, DB]
A --> C[CPU-bound\nComputation, image processing]
B --> D[asyncio / threading\ncan be effective — GIL released during I/O]
C --> E[multiprocessing\nor ProcessPoolExecutor]
C --> F[C/Cython extensions\nor NumPy which releases the GIL]
D --> G[Good for\nweb scraping, API calls, bots]
E --> H[Good for\nML preprocessing, batch compute]
F --> I[Good for\nscientific computing]The GIL is not a problem for:
- Web servers (I/O-bound — the GIL is released while waiting on network/DB)
- Data science with NumPy/pandas (NumPy operations release the GIL)
- Asyncio (single-threaded, the GIL is irrelevant)
The GIL becomes a problem for:
- Parallel computation in pure Python across multiple threads
- CPU-intensive workloads needing true parallelism in threads
The solutions: multiprocessing for CPU-bound, asyncio or threading for I/O-bound.
When to Choose Python #
Choose Python if:
✓ You work in data science, machine learning, or AI — Python has no rival here
✓ You need scripting or automation — Python is king in this domain
✓ You're building web APIs with high development speed needs
✓ You're in research or academia — the scientific Python ecosystem is unmatched
✓ You're building prototypes or MVPs needing fast iteration
✓ New or diverse-background teams — Python is the easiest to learn
Consider alternatives if:
✗ You need high performance for CPU-intensive tasks → Go, Rust, C++
✗ You're building mobile apps → Flutter/Dart, Swift, Kotlin
✗ You need massive concurrency with thousands of goroutines/threads → Go
✗ Memory footprint is a top priority → Go, Rust
✗ You're building embedded systems with tight constraints → C, MicroPython
✗ Absolute compile-time type safety is a requirement → Rust, Go, Kotlin
| Criterion | Python | Go | Rust | JavaScript |
|---|---|---|---|---|
| Ease of learning | ★★★★★ | ★★★★☆ | ★★☆☆☆ | ★★★★☆ |
| Performance | ★★☆☆☆ | ★★★★★ | ★★★★★ | ★★★☆☆ |
| ML/AI ecosystem | ★★★★★ | ★☆☆☆☆ | ★★☆☆☆ | ★★☆☆☆ |
| Scripting/Automation | ★★★★★ | ★★★☆☆ | ★★☆☆☆ | ★★★☆☆ |
| Web APIs | ★★★★☆ | ★★★★★ | ★★★☆☆ | ★★★★★ |
| Concurrency | ★★★☆☆ | ★★★★★ | ★★★★☆ | ★★★★☆ |
FAQ #
Is Python slow and does it matter?
Python CPython is indeed slower than Go, Rust, or Java for CPU-intensive computation. But for most real applications, the bottleneck is I/O (database, network), not the language. A Django or FastAPI web server responding in 50ms — is “slow” Python relevant there? For ML/AI, the libraries used (NumPy, PyTorch) are written in C/C++ and CUDA — Python is just the orchestrator. Python 3.11 is also already 40–60% faster than 3.10.
What’s the difference between requirements.txt and pyproject.toml?
requirements.txt is an old format that only records the package list and versions — flat, no project metadata. pyproject.toml is the modern standard (PEP 517/518) that unifies build system configuration, dependencies, and tooling (mypy, pytest, ruff) in one file. For new projects, always use pyproject.toml. For deployment, use uv lock or pip freeze to produce a reproducible lockfile.
When should I use FastAPI vs Django vs Flask?
Django: a full-stack framework with an ORM, admin panel, built-in auth — suitable for traditional web apps with many built-in features. Flask: a minimal micro-framework, you pick every component yourself — suitable for simple APIs or when you need full control. FastAPI: a modern async-based framework with type hints and OpenAPI auto-generation — the best choice for REST APIs or microservices needing performance and automatic documentation.
What is __init__.py and when is it needed?
__init__.py is the file that marks a directory as a Python package — allowing that directory to be imported. In Python 3.3+, “namespace packages” exist that don’t require __init__.py, but for regular packages that need distribution or organized initialization, __init__.py remains relevant and recommended.
Is Python good for competitive programming?
Python is popular in competitive programming because of its concise syntax and rich built-in libraries (collections, heapq, itertools, math). The drawback: Python can TLE (Time Limit Exceeded) on problems needing high performance. Solutions: use PyPy if the platform supports it, or implement critical parts with optimized built-in data structures like collections.deque and heapq.
Summary #
- Python was born for human readability — The Zen of Python isn’t a slogan, it’s a real design principle. Indentation as syntax, one obvious way to do things, explicit over implicit — all of this shapes the language’s unique character.
- Python 3.11 and 3.12 are much faster — The Faster CPython project made Python 3.11 40–60% faster than 3.10. Always upgrade to the latest stable version. Python 2 has been EOL since 2020 — no reason to use Python 2.
- Type hints are a modern best practice — Use type hints in all production code. They don’t change runtime behavior but provide huge benefits: automatic documentation, IDE support, and earlier bug detection with mypy or pyright.
- Virtual environments are required for every project — Don’t install packages globally. Use
python -m venvor modern tools likeuvfor per-project dependency isolation.- The GIL isn’t a problem for most use cases — I/O-bound tasks (web servers, API calls) aren’t affected by the GIL. For CPU-bound, use
multiprocessing. For async I/O, useasyncio.- Python is irreplaceable in ML/AI — PyTorch, TensorFlow, scikit-learn, HuggingFace Transformers — the Python ML ecosystem has no equal in any language. If you work in AI/ML, Python is the only practical choice.
- Comprehensions and decorators are Python idioms — List/dict/set comprehensions for collection transformations, decorators for cross-cutting concerns. Pythonic code leverages both well.
- uv and ruff are modern Python tooling — uv replaces pip+virtualenv with 10–100x faster speed. ruff replaces flake8+isort+black in one tool that’s also much faster. For new projects, use both from the start.
Next: Installation →