UUIDs (Universally Unique Identifiers) provide unique IDs without coordination. Essential for distributed systems, database keys, and API resources.
UUID Versions
import uuid
# UUID v4: Random (most common)
random_id = uuid.uuid4()
print(random_id) # e.g., 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
# UUID v1: Timestamp + MAC address
time_based = uuid.uuid1()
# UUID v3: MD5 hash of namespace + name
md5_based = uuid.uuid3(uuid.NAMESPACE_DNS, 'example.com')
# UUID v5: SHA-1 hash of namespace + name (preferred over v3)
sha1_based = uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')When to Use Each Version
| Version | Uniqueness | Sortable | Privacy | Use Case |
|---|---|---|---|---|
| v1 | Guaranteed | Yes | Leaks MAC | Internal systems |
| v3 | Deterministic | No | Safe | Reproducible IDs |
| v4 | Random | No | Safe | General purpose |
| v5 | Deterministic | No | Safe | Reproducible IDs |
UUID Properties
import uuid
u = uuid.uuid4()
# String representations
print(str(u)) # 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
print(u.hex) # 'f47ac10b58cc4372a5670e02b2c3d479'
print(u.urn) # 'urn:uuid:f47ac10b-58cc-4372-a567-0e02b2c3d479'
# Components
print(u.version) # 4
print(u.variant) # RFC_4122
print(u.bytes) # b'\xf4z\xc1\x0bX\xccCr\xa5g\x0e\x02\xb2\xc3\xd4y'
print(u.int) # 324644427883734833893486402954248461433
# Time component (v1 only)
u1 = uuid.uuid1()
print(u1.time) # Timestamp
print(u1.node) # MAC addressCreating UUIDs from Strings
import uuid
# From string
u = uuid.UUID('f47ac10b-58cc-4372-a567-0e02b2c3d479')
# From hex (no dashes)
u = uuid.UUID(hex='f47ac10b58cc4372a5670e02b2c3d479')
# From bytes
u = uuid.UUID(bytes=b'\xf4z\xc1\x0bX\xccCr\xa5g\x0e\x02\xb2\xc3\xd4y')
# From integer
u = uuid.UUID(int=324644427883734833893486402954248461433)
# Validation
def is_valid_uuid(s: str) -> bool:
try:
uuid.UUID(s)
return True
except ValueError:
return FalseDeterministic UUIDs
Generate the same UUID for the same input:
import uuid
# Built-in namespaces
uuid.NAMESPACE_DNS # For domain names
uuid.NAMESPACE_URL # For URLs
uuid.NAMESPACE_OID # For OIDs
uuid.NAMESPACE_X500 # For X.500 DNs
# Same input = same UUID
id1 = uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')
id2 = uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')
print(id1 == id2) # True
# Custom namespace
MY_NAMESPACE = uuid.uuid5(uuid.NAMESPACE_DNS, 'myapp.example.com')
def user_uuid(username: str) -> uuid.UUID:
"""Generate deterministic UUID for username."""
return uuid.uuid5(MY_NAMESPACE, username)
print(user_uuid('alice')) # Always same UUID for 'alice'Database Primary Keys
import uuid
from dataclasses import dataclass, field
@dataclass
class User:
name: str
email: str
id: uuid.UUID = field(default_factory=uuid.uuid4)
# SQLAlchemy
from sqlalchemy import Column
from sqlalchemy.dialects.postgresql import UUID
class User(Base):
__tablename__ = 'users'
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)Short IDs from UUIDs
import uuid
import base64
def uuid_to_short(u: uuid.UUID) -> str:
"""Convert UUID to short URL-safe string."""
return base64.urlsafe_b64encode(u.bytes).rstrip(b'=').decode()
def short_to_uuid(s: str) -> uuid.UUID:
"""Convert short string back to UUID."""
# Add padding back
padding = 4 - len(s) % 4
if padding != 4:
s += '=' * padding
return uuid.UUID(bytes=base64.urlsafe_b64decode(s))
# Usage
u = uuid.uuid4()
short = uuid_to_short(u)
print(f"UUID: {u}")
print(f"Short: {short}") # 22 chars instead of 36
restored = short_to_uuid(short)
print(u == restored) # TrueSortable UUIDs (ULID-style)
UUIDs aren't naturally sortable. Here's a time-prefixed approach:
import uuid
import time
import struct
def time_uuid() -> str:
"""Generate time-sortable UUID-like identifier."""
# Timestamp prefix (milliseconds)
timestamp = int(time.time() * 1000)
time_bytes = struct.pack('>Q', timestamp)[-6:] # 48 bits
# Random suffix
random_bytes = uuid.uuid4().bytes[:10]
# Combine and format like UUID
combined = time_bytes + random_bytes
hex_str = combined.hex()
return f"{hex_str[:8]}-{hex_str[8:12]}-{hex_str[12:16]}-{hex_str[16:20]}-{hex_str[20:]}"
# These sort chronologically
id1 = time_uuid()
time.sleep(0.01)
id2 = time_uuid()
print(id1 < id2) # TrueUUID Sets and Dicts
import uuid
# UUIDs are hashable
seen_ids = set()
u = uuid.uuid4()
seen_ids.add(u)
print(u in seen_ids) # True
# As dict keys
users = {
uuid.uuid4(): {'name': 'Alice'},
uuid.uuid4(): {'name': 'Bob'},
}
for user_id, data in users.items():
print(f"{user_id}: {data['name']}")Nil UUID
The all-zeros UUID for representing absence:
import uuid
nil_uuid = uuid.UUID('00000000-0000-0000-0000-000000000000')
# or
nil_uuid = uuid.UUID(int=0)
def get_parent_id(item) -> uuid.UUID | None:
"""Return parent ID or None."""
parent = item.get('parent_id')
if parent == nil_uuid:
return None
return parentComparing UUIDs
import uuid
u1 = uuid.uuid4()
u2 = uuid.uuid4()
# Equality
print(u1 == u2) # False (almost certainly)
# Compare with string
print(u1 == str(u1)) # False (different types!)
print(str(u1) == str(u1)) # True
# Ordering (lexicographic)
print(u1 < u2) # Based on bytes comparisonRequest Tracing
import uuid
import logging
from contextvars import ContextVar
request_id: ContextVar[str] = ContextVar('request_id')
class RequestIDFilter(logging.Filter):
def filter(self, record):
record.request_id = request_id.get('unknown')
return True
def process_request(handler):
"""Middleware to add request ID."""
def wrapper(request, *args, **kwargs):
req_id = str(uuid.uuid4())
request_id.set(req_id)
request.headers['X-Request-ID'] = req_id
return handler(request, *args, **kwargs)
return wrapperBulk UUID Generation
import uuid
# Generate many UUIDs efficiently
def generate_uuids(count: int) -> list[uuid.UUID]:
return [uuid.uuid4() for _ in range(count)]
# Pre-generate for batches
uuids = generate_uuids(1000)
# Assign to objects
for obj, uid in zip(objects, uuids):
obj.id = uidBest Practices
- Use v4 for random IDs: Secure and simple
- Use v5 for deterministic IDs: When you need same input → same output
- Store as native UUID: Don't store as string in databases
- Validate on input: Always parse user-provided UUIDs
- Consider collision probability: v4 has 2^122 possibilities—practically collision-free
UUIDs solve the "unique ID without coordination" problem. They're the standard choice for distributed systems and APIs.
React to this post: