Singleton behavior is a foundational runtime concept that influences memory usage, identity comparison, concurrency guarantees, and API design. Both Kotlin and Python implement singleton semantics at the language/runtime level — but in different ways and with different guarantees.
This article focuses on:
- What singleton means at runtime
- How Kotlin implements shared instances
- How Python guarantees
Noneas a singleton - Production-ready patterns
- Correct identity comparisons
Kotlin: Shared Instances and Native Singleton Support


4
1️⃣ emptyList() — Shared Immutable Instance
val list1 = emptyList<Int>()
val list2 = emptyList<Int>()
println(list1 === list2) // true
Why This Works
emptyList()returns a shared immutable object.- Kotlin internally uses a single
EmptyListinstance. - Because it is immutable, sharing is safe.
- No additional allocation occurs after initial use.
Production Implications
- Memory efficient
- Thread-safe
- Zero mutation risk
- Safe for high-concurrency systems
This is an intentional JVM-level optimization.
2️⃣ Kotlin object — First-Class Singleton
Kotlin provides a native singleton construct:
object ConfigManager {
var environment: String = "prod"
}
Usage:
ConfigManager.environment = "staging"
Runtime Guarantees
- Lazily initialized
- Thread-safe initialization on JVM
- Exactly one instance per classloader
When to Use in Production
Use object for:
- Stateless services
- Configuration holders
- Registries
- Caches with controlled mutation
Avoid using it for:
- Complex dependency graphs (prefer DI frameworks)
- Mutable global state without synchronization
- Highly test-coupled systems
Python: Runtime Singletons and Identity Guarantees

4
1️⃣ None — A True Language-Level Singleton
value = None
value1 = None
print(value is value1) # True
Why This Is Guaranteed
- CPython defines a single internal object:
Py_None - All references to
Nonepoint to that object - The singleton guarantee is part of the language specification
There is exactly one None instance per interpreter.
Production Rule: Always Use Identity Comparison
Correct:
if value is None:
handle_missing()
Incorrect:
if value == None: # Avoid
handle_missing()
Reason:
==invokes__eq__- Custom objects can override equality
isguarantees identity check
This is a production-critical distinction.
Production-Ready Singleton Patterns in Python
Python does not provide a built-in object keyword like Kotlin. Singleton behavior must be implemented deliberately.
Option 1 — Module-Level Singleton (Recommended)
Python modules are naturally singletons.
config.py
class Config:
def __init__(self):
self.environment = "prod"
config = Config()
Usage:
from config import config
config.environment = "staging"
Why This Is Production-Safe
- Modules load once per interpreter process
- Clear and explicit
- No metaclass complexity
- Easy to mock in tests
This is the preferred approach in real systems.
Option 2 — Thread-Safe Lazy Singleton Class
import threading
class Singleton:
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
Characteristics
- Lazy initialization
- Thread-safe
- Suitable for shared infrastructure components
Use only when a module-level singleton is insufficient.
Identity vs Structural Equality
Kotlin
val a = emptyList<Int>()
val b = emptyList<Int>()
println(a === b) // identity
println(a == b) // structural equality
===→ same object==→ callsequals()
Python
a = None
b = None
print(a is b) # identity
print(a == b) # equality
is→ object identity==→ invokes__eq__
Understanding this distinction prevents subtle production bugs.
Design Trade-offs
| Feature | Kotlin | Python |
|---|---|---|
| Null singleton | null | None |
| Native singleton construct | object | No |
| Identity operator | === | is |
| Structural equality | == | == |
| Thread-safe singleton initialization | Yes (JVM) | Manual implementation |
When to Avoid Singletons
Do not use singletons when:
- You need multiple runtime configurations
- You rely heavily on dependency injection
- You require strict test isolation
- You manage lifecycle-scoped components
Modern backend architectures (Spring, Ktor, FastAPI, Django) typically prefer scoped dependency injection over global singletons.
Final Takeaways
- Kotlin provides built-in, thread-safe singleton support.
emptyList()returns a shared immutable instance.- Python guarantees
Noneas a true singleton. - Always use identity operators for singleton comparison.
- Prefer module-level singletons in Python for production systems.

