Examples and Interview Questions¶
Real-world applications and common interview topics related to Python variables, scope, and memory.
Real-World Examples¶
Configuration Management¶
class Config:
"""Application configuration with defaults and overrides."""
_defaults = {
'debug': False,
'log_level': 'INFO',
'max_connections': 100,
}
def __init__(self):
self._config = dict(self._defaults)
def get(self, key, default=None):
return self._config.get(key, default)
def set(self, key, value):
self._config[key] = value
def update_from_env(self):
import os
for key in self._defaults:
env_key = f'APP_{key.upper()}'
if env_key in os.environ:
self._config[key] = os.environ[env_key]
# Singleton pattern
_config = None
def get_config():
global _config
if _config is None:
_config = Config()
return _config
Caching Decorator¶
from functools import wraps
import time
def cached(ttl_seconds=60):
"""Cache function results with time-to-live."""
def decorator(func):
cache = {}
@wraps(func)
def wrapper(*args):
now = time.time()
# Check cache
if args in cache:
result, timestamp = cache[args]
if now - timestamp < ttl_seconds:
return result
# Compute and cache
result = func(*args)
cache[args] = (result, now)
return result
wrapper.cache_clear = lambda: cache.clear()
return wrapper
return decorator
@cached(ttl_seconds=300)
def expensive_api_call(user_id):
# Simulated API call
return fetch_user_data(user_id)
Event System¶
from collections import defaultdict
from weakref import WeakMethod, ref
class EventEmitter:
"""Publish-subscribe event system."""
def __init__(self):
self._handlers = defaultdict(list)
def on(self, event, handler):
"""Register event handler."""
# Use weak reference to avoid memory leaks
if hasattr(handler, '__self__'):
weak_handler = WeakMethod(handler)
else:
weak_handler = ref(handler)
self._handlers[event].append(weak_handler)
def emit(self, event, *args, **kwargs):
"""Emit event to all handlers."""
handlers = self._handlers[event]
# Clean up dead references
self._handlers[event] = [
h for h in handlers if h() is not None
]
for weak_handler in self._handlers[event]:
handler = weak_handler()
if handler:
handler(*args, **kwargs)
# Usage
emitter = EventEmitter()
def on_user_login(user):
print(f"User logged in: {user}")
emitter.on('login', on_user_login)
emitter.emit('login', 'Alice')
Object Pool¶
from contextlib import contextmanager
from threading import Lock
class ConnectionPool:
"""Thread-safe database connection pool."""
def __init__(self, create_conn, max_size=10):
self._create = create_conn
self._max_size = max_size
self._pool = []
self._in_use = 0
self._lock = Lock()
@contextmanager
def connection(self):
conn = self._acquire()
try:
yield conn
finally:
self._release(conn)
def _acquire(self):
with self._lock:
if self._pool:
return self._pool.pop()
if self._in_use < self._max_size:
self._in_use += 1
return self._create()
raise RuntimeError("Connection pool exhausted")
def _release(self, conn):
with self._lock:
self._pool.append(conn)
# Usage
pool = ConnectionPool(lambda: create_db_connection(), max_size=5)
with pool.connection() as conn:
conn.execute("SELECT * FROM users")
Interview Questions¶
Q1: What is the output?¶
def create_multipliers():
return [lambda x: i * x for i in range(5)]
multipliers = create_multipliers()
print([m(2) for m in multipliers])
Answer: [8, 8, 8, 8, 8]
Explanation: Due to late binding, all lambdas capture i by reference. When called, i has its final value of 4.
Fix: [lambda x, i=i: i * x for i in range(5)]
Q2: Explain the output¶
def append_to(element, to=[]):
to.append(element)
return to
print(append_to(1))
print(append_to(2))
print(append_to(3))
Answer: [1], [1, 2], [1, 2, 3]
Explanation: Mutable default arguments are evaluated once at function definition, not each call.
Q3: What's the difference between is and ==?¶
a = [1, 2, 3]
b = [1, 2, 3]
c = a
print(a == b) # ?
print(a is b) # ?
print(a is c) # ?
Answer: True, False, True
Explanation:
- == compares values (equality)
- is compares identity (same object in memory)
Q4: What will this print?¶
x = 10
def outer():
x = 20
def inner():
x = 30
print(x)
inner()
print(x)
outer()
print(x)
Answer: 30, 20, 10
Explanation: Each function has its own local x. Without global or nonlocal, assignments create new local variables.
Q5: Fix this closure¶
def make_counter():
count = 0
def counter():
count += 1 # UnboundLocalError!
return count
return counter
Fixed version:
def make_counter():
count = 0
def counter():
nonlocal count
count += 1
return count
return counter
Q6: What's wrong with this code?¶
class Cache:
data = {}
def set(self, key, value):
self.data[key] = value
c1 = Cache()
c2 = Cache()
c1.set('a', 1)
print(c2.data)
Answer: Prints {'a': 1} - unintended sharing
Explanation: data is a class variable, shared by all instances.
Fix:
class Cache:
def __init__(self):
self.data = {} # Instance variable
Q7: Memory question¶
import sys
a = [1, 2, 3]
b = a
c = a[:]
print(sys.getsizeof(a))
print(a is b)
print(a is c)
print(a == c)
Answer: ~88 (varies), True, False, True
Explanation:
- b = a creates another reference to same object
- c = a[:] creates a shallow copy (new object)
Q8: What does __slots__ do?¶
class Point:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
Answer: __slots__ eliminates the per-instance __dict__, reducing memory usage. Instances can only have the declared attributes.
Q9: Generator vs List Comprehension¶
# Which uses less memory?
squares_list = [x**2 for x in range(1000000)]
squares_gen = (x**2 for x in range(1000000))
Answer: The generator uses far less memory because it produces values on-demand instead of storing all in memory.
Q10: Explain garbage collection¶
Answer:
- Python uses reference counting as primary mechanism
- Each object tracks how many references point to it
- When count reaches 0, memory is freed
- Cycle detector handles circular references
- gc module provides control over garbage collection
Summary¶
Key Interview Topics¶
| Topic | What to Know |
|---|---|
| Late binding | Closures capture by reference |
| Mutable defaults | Evaluated once at definition |
is vs == |
Identity vs equality |
| Scope | LEGB rule, global, nonlocal |
__slots__ |
Memory optimization |
| Generators | Lazy evaluation |
| GC | Reference counting + cycle detection |
Red Flags to Watch For¶
- Mutable default arguments
- Late binding in loops
- Class variables vs instance variables
- Missing
nonlocalorglobal - Using
isfor value comparison