uuid.uuid4
The whole implementation is UUID(bytes=os.urandom(16), version=4): 16 bytes from the operating system's secure random source, with 6 bits overwritten to mark version 4. Different on every call; str() of it is the familiar 36-character ID.
Demo
import uuid uuid.UUID(bytes=bytes.fromhex('00000000000000000000000000000000'), version=4)
Even all-zero bytes come out as version 4: the third group becomes 4000 and the fourth 8000, because version=4 forces the version digit to 4 and the variant bits to 10. All-ff bytes keep every other bit, giving ffffffff-ffff-4fff-bfff-ffffffffffff. bytes.fromhex skips spaces between bytes; 15 bytes are refused by UUID. In the second tab 1000 fresh UUIDs are 1000 distinct values, all version 4, all 36 characters.
Common patterns
import uuid request_id = str(uuid.uuid4())
import uuid from dataclasses import dataclass, field @dataclass class Job: name: str id: uuid.UUID = field(default_factory=uuid.uuid4)
import uuid path = f'/tmp/upload-{uuid.uuid4().hex}.bin'
Examples
Pitfalls
import uuid def make(id=uuid.uuid4()): return id make() == make()
import uuid def make(id=None): return id or uuid.uuid4() make() == make()
import uuid u = uuid.uuid4() u == str(u)
import uuid u = uuid.uuid4() str(u) == str(u)
import uuid uuid.uuid4() == uuid.uuid4()
import uuid uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com') == uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')
When to use
- Database primary keys and public IDs that must not reveal anything
- Request, trace, job and upload IDs
- Any time you need a unique ID without coordination
- IDs that must be reproducible from data → uuid5
- Secret tokens (session keys, reset links) → secrets.token_urlsafe()
- Keys that should sort by creation time → a timestamp column, or uuid7() on 3.14+
Notes
FAQ
In theory yes, in practice no: it has 122 random bits, so you would need about 2.7 * 10**18 UUIDs for a 50 percent chance of a single repeat.