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.

uuid functionPython 2.5+Live demo
Common call
str(uuid.uuid4())
Returns
a new 36-character string like '16fd2706-8baf-433b-82eb-8c7fada847da'
Replaces
Counters, timestamps or random.random() as IDs
Watch out
Returns a UUID object, not a str: wrap in str() for JSON and string comparisons
uuid.uuid4()
→ uuid.UUID

Demo

Live evaluation
uuid4() feeds 16 random bytes into UUID(..., version=4). Type the 16 bytes as hex to see what the version bits do to them.
Try:
Inputs
hexstr32 hex digits (16 bytes)
Code
import uuid
uuid.UUID(bytes=bytes.fromhex('00000000000000000000000000000000'), version=4)
Result
UUID('00000000-0000-4000-8000-000000000000')

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

An ID as text
For JSON, URLs, log lines and string columns.
import uuid
request_id = str(uuid.uuid4())
A dataclass with a fresh ID per instance
default_factory runs uuid4 for every new object (a plain default would share one ID).
import uuid
from dataclasses import dataclass, field

@dataclass
class Job:
    name: str
    id: uuid.UUID = field(default_factory=uuid.uuid4)
Unique temporary file names
hex has no hyphens and a fixed length of 32.
import uuid
path = f'/tmp/upload-{uuid.uuid4().hex}.bin'

Examples

1. Always version 4, RFC 4122 variant
import uuid u = uuid.uuid4() (u.version, u.variant)
Returns
(4, 'specified in RFC 4122')
2. A UUID object, not a str
import uuid type(uuid.uuid4())
Returns
<class 'uuid.UUID'>
3. 36 characters as text
import uuid len(str(uuid.uuid4()))
Returns
36
4. The version digit is always 4
import uuid str(uuid.uuid4())[14]
Returns
'4'
5. The variant digit is 8, 9, a or b
import uuid str(uuid.uuid4())[19] in '89ab'
Returns
True
6. No repeats in 10,000
import uuid len({uuid.uuid4() for _ in range(10_000)})
Returns
10000
7. Same thing, by hand
import os, uuid uuid.UUID(bytes=os.urandom(16), version=4).version
Returns
4

Pitfalls

1. Calling uuid4 once as a default
A default argument is evaluated once, when the function is defined, so every call shares the same "new" ID.
def f(id=uuid4())
import uuid
def make(id=uuid.uuid4()):
    return id
make() == make()
True
id=None
import uuid
def make(id=None):
    return id or uuid.uuid4()
make() == make()
False
2. Comparing the UUID with a string
uuid4() returns a UUID. Comparing it, or using it as a dict key, against strings never matches.
UUID vs str
import uuid
u = uuid.uuid4()
u == str(u)
False
str on both sides
import uuid
u = uuid.uuid4()
str(u) == str(u)
True
3. Expecting the same UUID twice
uuid4 has no input, so it cannot be reproduced. For a stable ID derived from data, use uuid5.
uuid4 twice
import uuid
uuid.uuid4() == uuid.uuid4()
False
uuid5 twice
import uuid
uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com') == uuid.uuid5(uuid.NAMESPACE_DNS, 'example.com')
True

When to use

Use it
  • 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
Reach for something else
  • 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

CPython impl
def uuid4(): return UUID(bytes=os.urandom(16), version=4) in Lib/uuid.py
Randomness
os.urandom reads the operating system's cryptographic random source; the 3.14 docs describe uuid4 as generated "in a cryptographically-secure method". 122 of the 128 bits are random
Collisions
With 122 random bits, the birthday bound puts a 50 percent chance of any repeat at about 2.7 * 10**18 UUIDs
is_safe
Always SafeUUID.unknown: is_safe concerns only uuid1

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.