Generare UUID v4 in Python — Guida uuid.uuid4()

·Backend Developer·Revisionato daDmitri Volkov·Pubblicato

Usa il Generatore UUID v4 gratuito direttamente nel tuo browser — nessuna installazione.

Prova Generatore UUID v4 online →

Ogni volta che ho bisogno di un identificatore resistente alle collisioni per una riga di database, una traccia API o un token di sessione, la risposta è generare UUID v4 in Python — una riga, zero dipendenze: uuid.uuid4(). Il modulo uuid integrato di Python usa os.urandom() per casualità crittograficamente sicura. Per un UUID rapido senza scrivere codice, il generatore UUID v4 online funziona all'istante. Questa guida tratta gli attributi dell'oggetto UUID, la generazione in blocco, la serializzazione JSON, l'archiviazione su database, la validazione, uuid-utils (~10× più veloce, implementato in Rust) e i quattro errori più comuni — tutto con Python 3.8+.

Punti chiave
  • uuid.uuid4() è integrato nella stdlib di Python — basta import uuid, nessun pip install necessario.
  • Il valore restituito è un oggetto uuid.UUID, non una stringa — usa str(), .hex o .bytes per scegliere la rappresentazione adatta al tuo livello di archiviazione.
  • UUID v4 usa 122 bit casuali da os.urandom() — crittograficamente sicuro, senza esposizione di indirizzi MAC o timestamp.
  • Per servizi ad alta frequenza, pip install uuid-utils è un sostituto diretto ~10 volte più veloce, implementato in Rust.
  • Non passare mai uuid.uuid4 (senza parentesi) come argomento predefinito direttamente in una dataclass o in un modello Pydantic — in quel caso tutte le istanze condividerebbero un singolo UUID.

Cos'è UUID v4?

Un UUID (Universally Unique Identifier) è un'etichetta a 128 bit formattata come 32 cifre esadecimali divise in cinque gruppi da trattini: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. La versione 4 è la variante più diffusa: 122 di quei 128 bit sono generati casualmente, e i restanti 6 bit codificano la versione (4) e la variante (RFC 4122). Non è presente nessun timestamp né identificatore dell'host — l'identificatore è completamente opaco e rispettoso della privacy. La probabilità che due UUID v4 generati indipendentemente collidano è talmente ridotta che nella pratica non accade mai, anche in sistemi distribuiti che generano milioni di ID al secondo.

Before · Python
After · Python
event_id = "evt-" + str(random.randint(100000, 999999))  # fragile, not unique
event_id = str(uuid.uuid4())  # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e

uuid.uuid4() — Il modo standard per generare UUID v4 in Python

Il modulo uuid fa parte della libreria standard di Python. Chiamare uuid.uuid4() restituisce un oggetto uuid.UUID con un insieme completo di attributi per diverse rappresentazioni. La conversione in stringa con str() produce il formato con trattini canonico che API, database e header HTTP si aspettano.

Python 3.8+ — esempio minimale
import uuid

# Genera un UUID v4
request_id = uuid.uuid4()

print(request_id)           # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
print(type(request_id))     # <class 'uuid.UUID'>
print(request_id.version)   # 4

# Converti in stringa per JSON / header HTTP
print(str(request_id))      # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"
print(request_id.hex)       # "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" (senza trattini)
print(request_id.bytes)     # b';...' (16 byte grezzi)

Un pattern comune nel mondo reale consiste nell'allegare un UUID a ogni richiesta API in uscita per poter correlare i log tra i servizi. Ecco un wrapper minimale per una sessione requests che inietta un UUID fresco in ogni chiamata:

Python 3.8+ — trace ID per richiesta
import uuid
import requests

def call_api(endpoint: str, payload: dict) -> dict:
    trace_id = str(uuid.uuid4())
    headers = {
        "X-Request-ID": trace_id,
        "Content-Type": "application/json",
    }
    response = requests.post(endpoint, json=payload, headers=headers, timeout=10)
    response.raise_for_status()
    return {"trace_id": trace_id, "data": response.json()}

# result["trace_id"] permette di trovare la richiesta esatta in tutti i log del servizio
result = call_api("https://api.example.com/v1/orders", {"product_id": "prod_7x2k", "qty": 3})
print(result["trace_id"])  # es. "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"

Per generare UUID in blocco — ad esempio, pre-popolare un batch di righe nel database — una list comprehension è idiomatica e leggibile:

Python 3.8+ — generazione in blocco
import uuid

# Pre-genera ID per 1000 eventi di telemetria
event_ids = [str(uuid.uuid4()) for _ in range(1000)]
print(f"Generated {len(event_ids)} unique IDs")
print(event_ids[0])   # es. "a1c2e3f4-..."
print(event_ids[-1])  # valore diverso ogni volta

Hai bisogno di un UUID rapido senza eseguire codice? Usa il generatore UUID v4 online per copiare un valore fresco con un clic, o generane centinaia in blocco — utile per popolare database di test o file di fixture.

Nota:uuid.uuid4() chiama os.urandom(16) internamente, poi imposta i bit 6–7 del byte 8 a 10 (variante) e i bit 12–15 del byte 6 a 0100 (versione 4). I restanti 122 bit sono casuali. Ecco perché non puoi fidarti della versione a meno che non analizzi con uuid.UUID().

Attributi e rappresentazioni dell'oggetto UUID

L'oggetto uuid.UUID espone più rappresentazioni dello stesso valore a 128 bit. Scegliere quella giusta per il proprio livello di archiviazione previene la corruzione silenziosa dei dati e lo spreco di byte.

Attributo / Metodo
Tipo
Descrizione
uuid.UUID(hex=...)
UUID
Analizza un UUID esistente da una stringa esadecimale, con o senza trattini.
.hex
str
Stringa esadecimale minuscola di 32 caratteri senza trattini — formato di archiviazione compatto.
.int
int
Rappresentazione intera a 128 bit dell'UUID — utile per operazioni aritmetiche e ordinamento.
.bytes
bytes
Rappresentazione binaria big-endian di 16 byte — dimensione di archiviazione più efficiente.
.bytes_le
bytes
Binario little-endian di 16 byte — corrisponde all'ordine dei byte dei GUID Microsoft.
.fields
tuple
Tupla di sei campi UUID: (time_low, time_mid, time_hi_version, clock_seq_hi_variant, clock_seq_low, node).
.version
int | None
Numero di versione UUID (1–5, o None per UUID non standard).
.variant
str
Stringa della variante UUID — "specified in RFC 4122" per UUID standard.
str(uuid_obj)
str
Stringa canonica di 36 caratteri con quattro trattini: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
Python 3.8+ — tutte le rappresentazioni
import uuid

u = uuid.uuid4()

print(str(u))         # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"  (36 caratteri)
print(u.hex)          # "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e"      (32 caratteri, senza trattini)
print(u.bytes)        # b';Š...'                       (16 byte, big-endian)
print(u.bytes_le)     # b'Š...'                       (16 byte, little-endian)
print(u.int)          # 78823... (intero a 128 bit)
print(u.version)      # 4
print(u.variant)      # 'specified in RFC 4122'

# Round-trip: ricostruzione dalla stringa
reconstructed = uuid.UUID("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e")
print(reconstructed == u)  # True (se u aveva quel valore)

Per PostgreSQL con psycopg2 o asyncpg, passa direttamente l'oggetto UUID — il driver gestisce la mappatura al tipo di colonna uuid nativo. Per SQLite, usa str(u) (TEXT) o u.bytes (BLOB, 16 byte contro 36 della stringa). Per efficienza di archiviazione su larga scala, .bytes è il 55% più piccolo rispetto alla stringa canonica.

Validazione e analisi di stringhe UUID v4 in Python

Ogni volta che un UUID arriva dall'input dell'utente, da un parametro di percorso URL o da un'API upstream, dovresti validarlo prima di usarlo come chiave di database. L'approccio idiomatico è tentare la costruzione con uuid.UUID() e intercettare ValueError. Puoi anche verificare che il valore in ingresso sia specificamente la versione 4 controllando .version.

Python 3.8+ — funzione di validazione
import uuid

def parse_uuid4(raw: str) -> uuid.UUID:
    """
    Analizza e valida una stringa UUID v4.
    Solleva ValueError per formato non valido o versione errata.
    """
    try:
        u = uuid.UUID(raw)
    except ValueError as exc:
        raise ValueError(f"Invalid UUID format: {raw!r}") from exc

    if u.version != 4:
        raise ValueError(f"Expected UUID v4, got v{u.version}: {raw!r}")

    return u

# Utilizzo in un handler di route FastAPI / Flask
def get_order(order_id: str):
    try:
        uid = parse_uuid4(order_id)
    except ValueError as exc:
        return {"error": str(exc)}, 400

    # sicuro da usare uid in una query DB
    return {"order_id": str(uid), "status": "processing"}
Nota:uuid.UUID() accetta stringhe con o senza trattini, e accetta anche il prefisso urn:uuid:. Quindi "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" (senza trattini) e "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" vengono entrambi analizzati nello stesso oggetto.

UUID v4 in payload JSON e risposte API

Lo standard JSON non ha un tipo UUID — un UUID in JSON è sempre una stringa. Questo significa che devi convertire l'oggetto uuid.UUID in stringa prima di passarlo a json.dumps(). L'approccio più pulito è una sottoclasse personalizzata di JSONEncoder così non devi disseminare chiamate str() in tutto il tuo codebase.

Python 3.8+ — JSONEncoder personalizzato per UUID
import json
import uuid
from datetime import datetime

class ApiEncoder(json.JSONEncoder):
    """Serializza oggetti UUID e datetime nelle risposte JSON."""
    def default(self, obj):
        if isinstance(obj, uuid.UUID):
            return str(obj)
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

# Risposta API realistica con UUID annidati
api_response = {
    "request_id": uuid.uuid4(),
    "created_at": datetime(2026, 3, 14, 9, 41, 0),
    "order": {
        "id": uuid.uuid4(),
        "customer_id": uuid.uuid4(),
        "items": [
            {"product_id": uuid.uuid4(), "sku": "NVX-9000", "qty": 2},
        ],
    },
}

print(json.dumps(api_response, indent=2, cls=ApiEncoder))
# {
#   "request_id": "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e",
#   "created_at": "2026-03-14T09:41:00",
#   "order": {
#     "id": "a1c2e3f4-...",
#     ...
#   }
# }

Per una singola chiamata di serializzazione, il parametro default= è più semplice della sottoclasse:

Python 3.8+ — hook default= (uso singolo)
import json, uuid

event_id = uuid.uuid4()
payload = {"event_id": event_id, "action": "checkout"}

# Passa un callable; chiamato solo per i tipi che json non sa gestire
json_str = json.dumps(payload, default=str)
print(json_str)  # {"event_id": "3b1f8a9d-...", "action": "checkout"}

Quando ricevi una risposta da un'API esterna, analizza le stringhe UUID e riconvertile in oggetti così il tuo codice ottiene il set completo di attributi e la type safety:

Python 3.8+ — analisi UUID da una risposta API
import json
import uuid
import requests

def fetch_shipment(shipment_id: str) -> dict:
    """Recupera una spedizione e restituisce con campi UUID tipizzati."""
    response = requests.get(
        f"https://api.logistics.example.com/v2/shipments/{shipment_id}",
        headers={"Accept": "application/json"},
        timeout=10,
    )
    response.raise_for_status()
    data = response.json()

    # Analizza i campi UUID e riconvertili in oggetti uuid.UUID
    try:
        data["id"] = uuid.UUID(data["id"])
        data["carrier_id"] = uuid.UUID(data["carrier_id"])
    except (KeyError, ValueError) as exc:
        raise RuntimeError(f"Malformed shipment response: {exc}") from exc

    return data

Per aggiornare i campi UUID in un file JSON su disco — ad esempio, ruotare un correlation ID in un file di configurazione o di seed — leggi, modifica e riscrivi atomicamente:

Python 3.8+ — leggi, aggiorna e scrivi un file JSON
import json, uuid

def rotate_correlation_id(path: str) -> str:
    """Sostituisce o aggiunge 'correlation_id' in un file JSON. Restituisce il nuovo UUID."""
    try:
        with open(path) as f:
            data = json.load(f)
    except FileNotFoundError:
        data = {}
    except json.JSONDecodeError as exc:
        raise ValueError(f"Invalid JSON in {path!r}: {exc}") from exc

    new_id = str(uuid.uuid4())
    data["correlation_id"] = new_id

    with open(path, "w") as f:
        json.dump(data, f, indent=2)

    return new_id

Se non vuoi eseguire uno script ogni volta che devi ispezionare un UUID da una risposta API, incollalo direttamente nell' UUID Decoder — mostra versione, variante e tutti i campi senza scrivere codice.

Genera UUID v4 dalla riga di comando con Python

Il modulo uuid di Python non espone un sottocomando CLI standalone come python -m json.tool, ma un one-liner copre lo stesso caso d'uso. Questi comandi sono utili negli script shell, nelle pipeline CI e ogni volta che hai bisogno di un identificatore temporaneo senza aprire un REPL.

bash
# UUID v4 singolo
python3 -c "import uuid; print(uuid.uuid4())"
# 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e

# Formato hex senza trattini — utile per nomi di file e variabili d'ambiente
python3 -c "import uuid; print(uuid.uuid4().hex)"
# 3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e

# Genera 5 UUID per uno script di seed batch
python3 -c "import uuid; [print(uuid.uuid4()) for _ in range(5)]"

# Usa in una variabile shell
DEPLOY_ID=$(python3 -c "import uuid; print(uuid.uuid4())")
echo "Deploying with ID: $DEPLOY_ID"
Nota:Su macOS e sulla maggior parte delle distribuzioni Linux, uuidgen (un'utility C) produce valori UUID v4 ed è più veloce per i puri script shell. Usa il one-liner Python quando sei già in un ambiente incentrato su Python e vuoi coerenza con il modo in cui gli UUID vengono generati nel codice della tua applicazione.

UUID v4 ad alte prestazioni con uuid-utils

Il modulo uuid.uuid4() della libreria standard è abbastanza veloce per la maggior parte delle applicazioni — con pochi microsecondi per chiamata gestisce comodamente migliaia di ID al secondo. Se stai generando UUID nel percorso critico di un servizio ad alta frequenza (inserimenti in blocco, telemetria per evento su larga scala, o generazione di ID di richiesta sotto carico pesante), uuid-utils è un sostituto diretto implementato in Rust che nei benchmark raggiunge circa 10 volte la velocità della stdlib.

bash — install
pip install uuid-utils
Python 3.8+ — sostituto diretto uuid-utils
# uuid_utils è un sostituto diretto del modulo uuid della stdlib
import uuid_utils as uuid

# Stessa API della stdlib
request_id = uuid.uuid4()
print(request_id)           # 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
print(str(request_id))      # stringa canonica
print(request_id.hex)       # hex senza trattini
print(request_id.version)   # 4

# Supporta anche v7 (ordinato per tempo, ottimo come chiave primaria nei DB)
time_ordered_id = uuid.uuid7()
print(time_ordered_id)      # inizia con il prefisso del timestamp corrente
Attenzione:La modalità predefinita di uuid-utils restituisce il proprio tipo di oggetto UUID, compatibile con la stdlib nella maggior parte dei casi. Se hai bisogno di controlli isinstance(u, uuid.UUID) stretti dalla libreria standard, usa la modalità compat: import uuid_utils.compat as uuid. La modalità compat è leggermente più lenta di quella predefinita ma comunque più veloce della stdlib.

UUID v4 nelle Dataclass e nei modelli Pydantic

Le dataclass di Python e i modelli Pydantic supportano entrambi i campi UUID in modo nativo. Il pattern fondamentale quando si usa UUID come default auto-generato è passare il riferimento alla funzione, non il risultato di una chiamata — altrimenti ogni istanza condivide lo stesso UUID.

Python 3.10+ — dataclass con default UUID
from dataclasses import dataclass, field
import uuid

@dataclass
class WorkerJob:
    job_id: uuid.UUID = field(default_factory=uuid.uuid4)
    worker_id: str = "worker-01"
    payload: dict = field(default_factory=dict)

job1 = WorkerJob(payload={"task": "resize_image", "src": "uploads/img_4932.png"})
job2 = WorkerJob(payload={"task": "send_email",   "to":  "ops@example.com"})

print(job1.job_id)  # univoco per istanza
print(job2.job_id)  # diverso da job1.job_id
print(job1.job_id == job2.job_id)  # False
Python 3.10+ — modello Pydantic v2 con UUID
from pydantic import BaseModel, Field
import uuid

class OrderEvent(BaseModel):
    event_id: uuid.UUID = Field(default_factory=uuid.uuid4)
    order_id: uuid.UUID
    status: str
    amount_cents: int

event = OrderEvent(
    order_id=uuid.UUID("a1c2e3f4-5b6d-4e7f-8c9d-0a1b2c3d4e5f"),
    status="payment_confirmed",
    amount_cents=4995,
)

print(event.model_dump_json(indent=2))
# {
#   "event_id": "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e",
#   "order_id": "a1c2e3f4-5b6d-4e7f-8c9d-0a1b2c3d4e5f",
#   "status": "payment_confirmed",
#   "amount_cents": 4995
# }
# Pydantic v2 serializza uuid.UUID come stringa automaticamente

Errori comuni nella generazione di UUID v4 in Python

Ho visto tutti e quattro questi pattern apparire nelle code review e negli incidenti in produzione — sono facili da perdere perché non sollevano un errore immediatamente.

Chiamare uuid4 senza parentesi come valore predefinito

Problema: Passare uuid.uuid4 (l'oggetto funzione) come valore predefinito in una dataclass o modello senza avvolgerlo in default_factory — Python valuta il default una sola volta alla definizione della classe, quindi ogni istanza condivide lo stesso UUID.

Soluzione: Usa default_factory=uuid.uuid4 nelle dataclass o Field(default_factory=uuid.uuid4) in Pydantic così un UUID fresco viene generato per ogni istanza.

Before · Python
After · Python
@dataclass
class Session:
    # SBAGLIATO: valutato una volta, tutte le istanze condividono questo UUID
    session_id: uuid.UUID = uuid.uuid4()
@dataclass
class Session:
    # CORRETTO: la factory viene chiamata per ogni istanza
    session_id: uuid.UUID = field(default_factory=uuid.uuid4)
Confrontare un oggetto UUID con una stringa semplice

Problema: Gli oggetti uuid.UUID non sono uguali alle stringhe semplici, quindi session_id == '3b1f8a9d-...' restituisce sempre False anche quando il valore corrisponde — rompendo silenziosamente le ricerche.

Soluzione: Confronta sempre UUID con UUID: avvolgi la stringa con uuid.UUID() prima di confrontare, oppure converti entrambi i lati in str().

Before · Python
After · Python
# Restituisce False anche quando i valori corrispondono
if record["session_id"] == "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e":
    revoke_session(record)
target = uuid.UUID("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e")
if record["session_id"] == target:   # entrambi sono uuid.UUID
    revoke_session(record)

# Oppure normalizza tutto in stringhe al confine:
if str(record["session_id"]) == str(target):
    revoke_session(record)
Archiviare .hex invece di str() perdendo i trattini

Problema: uuid_obj.hex produce una stringa di 32 caratteri senza trattini. Se il codice a valle si aspetta il formato canonico di 36 caratteri con trattini (come la maggior parte delle API e dei database), lo rifiuterà o lo interpreterà silenziosamente in modo errato.

Soluzione: Usa str(uuid_obj) per il formato canonico di 36 caratteri a meno che tu non abbia un requisito esplicito per la forma hex compatta.

Before · Python
After · Python
# Archivia "3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e" — senza trattini
payload = {"correlation_id": request_id.hex}
# Archivia "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e" — formato standard
payload = {"correlation_id": str(request_id)}
Usare random.random() o secrets.token_hex() quando è atteso un UUID

Problema: random.random() non è crittograficamente sicuro, e secrets.token_hex(16) produce una stringa hex di 32 caratteri che non è un UUID valido — i validatori a valle che chiamano uuid.UUID() su di essa solleveranno ValueError.

Soluzione: Usa uuid.uuid4() ogni volta che il sistema ricevente si aspetta un identificatore in formato UUID. Usa secrets.token_hex() solo quando hai esplicitamente bisogno di un token casuale che non sia in formato UUID.

Before · Python
After · Python
import random, secrets

# Non è un UUID — fallirà la validazione uuid.UUID()
request_id = secrets.token_hex(16)   # "a1b2c3d4e5f6..."
session_id = str(random.random())    # "0.8273..." — non è nemmeno vicino
import uuid

request_id = str(uuid.uuid4())  # "3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"
# UUID v4 valido, crittograficamente sicuro

Metodi di generazione UUID in Python — Confronto rapido

Tutti i metodi qui sotto producono identificatori a 128 bit, ma differiscono per sorgente di entropia, caratteristiche di privacy e necessità di installazione di terze parti.

Metodo
Sorgente
Unicità
Privacy-safe
Tipi personalizzati
Velocità
Installazione
uuid.uuid4()
Random (os.urandom)
2¹²² bit casuali
N/A
Standard
Libreria standard
uuid.uuid1()
Timestamp + MAC
Tempo + host
❌ (MAC esposto)
N/A
Standard
Libreria standard
uuid.uuid3(name)
Hash MD5
Deterministico
namespace+name
Standard
Libreria standard
uuid.uuid5(name)
Hash SHA-1
Deterministico
namespace+name
Standard
Libreria standard
uuid_utils.uuid4()
Rust os.urandom
2¹²² bit casuali
N/A
~10× più veloce
pip install uuid-utils
secrets.token_hex(16)
os.urandom
128 bit casuali
N/A
Veloce
Libreria standard
str(uuid.uuid4())
Random
2¹²² bit casuali
N/A
Standard
Libreria standard

Usa uuid.uuid4() per identificatori univoci generici in applicazioni web, sistemi distribuiti e chiavi primarie di database quando l'ordinabilità non è richiesta. Usa uuid.uuid5() (o v3) per ID deterministici derivati da un namespace e un nome noti — ad esempio, generare un ID stabile per un URL canonico. Passa a uuid_utils.uuid7() quando hai bisogno di ID ordinati per tempo per gli indici del database (evita le divisioni di pagina negli indici B-tree ad alto tasso di inserimento). Scegli uuid_utils.uuid4() quando la velocità di generazione grezza è il collo di bottiglia.

UUID v4 vs UUID v7 — Quale usare?

La domanda pratica più comune è se usare UUID v4 o il più recente UUID v7 per le chiavi primarie del database. La risposta breve è: usa UUID v4 di default; passa a UUID v7 solo quando la frammentazione degli indici è un problema misurato.

I valori UUID v4 sono completamente casuali, il che significa che gli inserimenti atterrano in posizioni casuali in un indice B-tree. A tassi di inserimento moderati (da centinaia a poche migliaia al secondo) questo va bene — l'indice entra nel buffer pool e le scritture casuali sono economiche. A tassi di inserimento molto elevati, il posizionamento casuale causa frequenti divisioni di pagina e cache miss, aumentando l'amplificazione in scrittura e rallentando le query.

UUID v7 incorpora un timestamp Unix con precisione al millisecondo nei bit più significativi, così le righe inserite vicine nel tempo atterrano anche vicine nell'indice. Questo dona agli indici B-tree (PostgreSQL, MySQL, SQLite) un comportamento simile a un intero auto-incrementale: le nuove righe si aggiungono sempre alla fine dell'indice, eliminando le divisioni di pagina. Il compromesso è che UUID v7 codifica un timestamp, che rivela il momento di creazione — evitalo per ID rivolti agli utenti dove il momento di creazione è sensibile.

In Python, UUID v7 non è ancora nella libreria standard (a partire da Python 3.12). Generalo con pip install uuid-utils e chiama uuid_utils.uuid7(). Restituisce un oggetto con lo stesso set di attributi di uuid.UUID, quindi la migrazione da v4 è una modifica su una riga nella factory degli ID.

Per un'alternativa con un solo clic senza alcuna configurazione Python, incolla la tua stringa UUID nel generatore e validatore UUID v4 — genera, valida e decodifica tutti i campi nel browser.

Domande frequenti

Come si genera un UUID v4 in Python?

Chiama uuid.uuid4() dal modulo uuid integrato di Python. Restituisce un oggetto UUID — convertilo in stringa con str() quando hai bisogno di una rappresentazione testuale. Il modulo è incluso nella libreria standard, quindi non è necessario installare nulla con pip.

Python
import uuid

session_id = uuid.uuid4()
print(session_id)           # es. 3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e
print(str(session_id))      # stessa stringa canonica
print(session_id.hex)       # 3b1f8a9d2c4e4f6a8b0d5e7c9f1a3d2e (senza trattini)

Qual è la differenza tra uuid.uuid4() e str(uuid.uuid4())?

uuid.uuid4() restituisce un oggetto UUID con attributi come .hex, .bytes, .int e .version. str(uuid.uuid4()) converte immediatamente quell'oggetto in una stringa canonica di 36 caratteri, scartando l'oggetto. Mantieni l'oggetto se hai bisogno di più rappresentazioni; convertilo in stringa al momento in cui passi il valore a un payload JSON, a un database o a un header HTTP.

Python
import uuid

u = uuid.uuid4()
print(type(u))          # <class 'uuid.UUID'>
print(u.version)        # 4
print(u.hex)            # hex a 32 caratteri, senza trattini
print(u.bytes)          # binario di 16 byte
print(str(u))           # stringa canonica di 36 caratteri con trattini

uuid.uuid4() è crittograficamente sicuro?

Sì. uuid.uuid4() di Python usa internamente os.urandom(), che legge dal generatore di numeri casuali crittograficamente sicuro del sistema operativo (/dev/urandom su Linux/macOS, CryptGenRandom su Windows). I 122 bit casuali rendono la probabilità di collisione trascurabile per qualsiasi carico di lavoro realistico. Non confonderlo con random.random(), che non è crittograficamente sicuro.

Python
import uuid, os

# uuid4 internamente chiama os.urandom(16)
raw = os.urandom(16)
# uuid4 imposta i bit di versione e variante prima di restituire il risultato
u = uuid.UUID(bytes=raw, version=4)
print(u)  # UUID v4 valido da byte casuali grezzi

Come si verifica che una stringa sia un UUID v4 valido in Python?

Analizza con uuid.UUID() e controlla l'attributo .version. Se la stringa non è un UUID valido, uuid.UUID() solleva un ValueError — intercettalo per gestire l'input non valido. Questo valida anche che il formato (trattini, lunghezza) sia corretto.

Python
import uuid

def is_valid_uuid4(value: str) -> bool:
    try:
        u = uuid.UUID(value)
        return u.version == 4
    except ValueError:
        return False

print(is_valid_uuid4("3b1f8a9d-2c4e-4f6a-8b0d-5e7c9f1a3d2e"))  # True
print(is_valid_uuid4("not-a-uuid"))                              # False
print(is_valid_uuid4("3b1f8a9d-2c4e-1f6a-8b0d-5e7c9f1a3d2e"))  # False (v1, non v4)

Come si archiviano gli UUID in un database PostgreSQL o SQLite da Python?

Con PostgreSQL (tramite psycopg2 o asyncpg), passa direttamente l'oggetto UUID — il driver lo adatta al tipo UUID nativo. Con SQLite, che non ha un tipo UUID nativo, archivia come TEXT usando str(uuid_obj) o come BLOB usando uuid_obj.bytes. SQLAlchemy ha un tipo di colonna UUID che gestisce questo automaticamente tra i vari dialetti.

Python
import uuid
import sqlite3

conn = sqlite3.connect(":memory:")
conn.execute("CREATE TABLE events (id TEXT PRIMARY KEY, name TEXT)")

event_id = uuid.uuid4()
conn.execute("INSERT INTO events VALUES (?, ?)", (str(event_id), "user_signup"))
conn.commit()

row = conn.execute("SELECT * FROM events").fetchone()
# Ricostruisci l'oggetto UUID dalla stringa archiviata
retrieved_id = uuid.UUID(row[0])
print(retrieved_id.version)  # 4

È possibile generare più UUID contemporaneamente in Python?

Sì — usa una list comprehension o un generator. Ogni chiamata a uuid.uuid4() è indipendente e garantisce di produrre un valore distinto. Per la generazione in blocco dove la velocità è importante, uuid-utils (implementato in Rust) è circa 10 volte più veloce della libreria standard.

Python
import uuid

# Genera 5 trace ID univoci per una richiesta batch
trace_ids = [str(uuid.uuid4()) for _ in range(5)]
for tid in trace_ids:
    print(tid)
# Ogni riga è un UUID v4 distinto

Strumenti correlati

  • UUID v4 GeneratorGenera valori UUID v4 istantaneamente nel browser — senza bisogno di un ambiente Python. Copia un singolo valore o generane centinaia in blocco.
  • UUID v7 GeneratorGenera valori UUID v7 ordinati per tempo — ordinabili per data di creazione, ideali come chiavi primarie nei database dove la frammentazione degli indici è un problema.
  • UUID DecoderIspeziona qualsiasi UUID — versione, variante, timestamp (v1/v7) e campi node — senza scrivere un parser da zero.
  • JWT DecoderDecodifica e ispeziona i token JWT, che spesso contengono claim UUID come subject (sub) o identificatori jti insieme agli UUID di sessione.
MS
Maria SantosBackend Developer

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.

DV
Dmitri VolkovRevisore tecnico

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.