uuid.uuid3 / uuid5

The hash of namespace.bytes + name (a str is encoded as UTF-8), cut to 16 bytes, with the version and variant bits set. No randomness: anyone with the same namespace and name computes the same UUID. Prefer uuid5; uuid3 exists for compatibility.

uuid functionPython 2.5+Live demo
Common call
uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
Returns
UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
Replaces
Lookup tables that map names to generated IDs
Watch out
namespace must be a UUID object, not its string; names are case- and byte-exact
uuid.uuid3(namespacenamespace — A UUID that scopes the names: one of NAMESPACE_DNS/URL/OID/X500, or your own (e.g. a uuid4 you generated once).type: uuid.UUID · required, name) · uuid.uuid5(namespace, namename — The name to hash. str is encoded with UTF-8; bytes are used as they are.type: str | bytes · required)
→ uuid.UUID

Demo

Live evaluation
Both versions of the same domain name. Change one character and the UUIDs change completely.
Try:
Inputs
namestra domain name
Code
import uuid
(uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org'), uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org'))
Result
(UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e'), UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d'))

python.org reproduces the two UUIDs printed in the Python docs. Python.org gives unrelated values: the hash sees different bytes. Non-ASCII names work because a str is encoded to UTF-8 first. The third tab shows that uuid5 does no normalization: a trailing slash or a capital letter is a different name, even where a browser would treat the URLs as the same. Normalize names yourself before hashing.

Parameters

NameTypeRequiredDescription
namespaceuuid.UUIDyesA UUID that scopes the names: one of NAMESPACE_DNS/URL/OID/X500, or your own (e.g. a uuid4 you generated once).
namestr | bytesyesThe name to hash. str is encoded with UTF-8; bytes are used as they are.

Return value

uuid.UUID — Version 3 (MD5) or version 5 (SHA-1), RFC 4122 variant.

Common patterns

Stable IDs for external records
Importing the same record twice yields the same key, so inserts become idempotent.
import uuid
APP_NS = uuid.UUID('1cc262f7-3046-59d8-af1f-724f1ed6b671')  # generated once, then fixed
record_id = uuid.uuid5(APP_NS, f'customer:{external_id}')
Normalize before hashing
Decide what counts as the same name (case, whitespace, trailing slashes) first.
import uuid
key = uuid.uuid5(uuid.NAMESPACE_DNS, hostname.strip().lower().rstrip('.'))
A namespace per kind of thing
Derive child namespaces from a URL, then names under them.
import uuid
USERS = uuid.uuid5(uuid.NAMESPACE_URL, 'https://example.com/users')
user_id = uuid.uuid5(USERS, str(42))

Examples

1. uuid5 of a domain
import uuid uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
Returns
UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
2. uuid3 of the same domain
import uuid uuid.uuid3(uuid.NAMESPACE_DNS, 'python.org')
Returns
UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')
3. Deterministic
import uuid uuid.uuid5(uuid.NAMESPACE_URL, 'https://example.com/') == uuid.uuid5(uuid.NAMESPACE_URL, 'https://example.com/')
Returns
True
4. bytes and str agree for ASCII
import uuid uuid.uuid5(uuid.NAMESPACE_DNS, b'python.org') == uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
Returns
True
5. A custom namespace
import uuid ns = uuid.uuid5(uuid.NAMESPACE_URL, 'https://example.com/') uuid.uuid5(ns, 'user:42')
Returns
UUID('1cc262f7-3046-59d8-af1f-724f1ed6b671')
6. Version 3 and version 5
import uuid (uuid.uuid3(uuid.NAMESPACE_DNS, 'a').version, uuid.uuid5(uuid.NAMESPACE_DNS, 'a').version)
Returns
(3, 5)
7. Same as hashlib by hand
import hashlib, uuid digest = hashlib.sha1(uuid.NAMESPACE_DNS.bytes + b'python.org').digest() uuid.UUID(bytes=digest[:16], version=5) == uuid.uuid5(uuid.NAMESPACE_DNS, 'python.org')
Returns
True

Pitfalls

1. Passing the namespace as a string
uuid5 reads namespace.bytes, which a str does not have. Parse the string with uuid.UUID() first.
str namespace
import uuid
uuid.uuid5('6ba7b810-9dad-11d1-80b4-00c04fd430c8', 'python.org')
AttributeError: 'str' object has no attribute 'bytes'
UUID(namespace)
import uuid
uuid.uuid5(uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8'), 'python.org')
UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')
2. Passing a number as the name
Only str and bytes are accepted; anything else fails when it is appended to the namespace bytes.
name=42
import uuid
uuid.uuid5(uuid.NAMESPACE_DNS, 42)
TypeError: can't concat int to bytes
str(42)
import uuid
uuid.uuid5(uuid.NAMESPACE_DNS, str(42)).version
5
3. Using name-based UUIDs as secrets
Anyone who knows (or guesses) the namespace and name computes the same UUID. They identify, they do not protect.
guessable
import uuid
uuid.uuid5(uuid.NAMESPACE_DNS, 'admin') == uuid.uuid5(uuid.NAMESPACE_DNS, 'admin')
True
random token
import secrets
len(secrets.token_urlsafe(32))
43

When to use

Use it
  • The same input must map to the same ID on every machine and every run
  • Idempotent imports and deduplication
  • IDs for URLs, domain names, file paths, external keys
Reach for something else
  • IDs that must not be guessable → uuid4 or secrets
  • New code choosing between the two → uuid5 (SHA-1) rather than uuid3 (MD5); RFC 4122 recommends version 5

Notes

CPython impl
Lib/uuid.py: name = bytes(name, "utf-8") if it is a str; digest = hashlib.md5(namespace.bytes + name, usedforsecurity=False) or hashlib.sha1(...); UUID(bytes=digest[:16], version=3 or 5)
Hashes
SHA-1 produces 20 bytes, of which the first 16 are used. Neither MD5 nor SHA-1 is collision-resistant today, which does not matter for naming but rules out security uses
Unicode
A str name with a lone surrogate cannot be encoded as UTF-8 and raises UnicodeEncodeError

FAQ

uuid.uuid5(namespace, text), for example uuid.uuid5(uuid.NAMESPACE_URL, 'https://example.com/'). The same namespace and text always give the same UUID, on any machine and any Python version.