A uuid.UUID Object Is Not Equal to Its Own String Form (tested)

A uuid.UUID object prints as a string, gets logged as a string, and looks exactly like a string everywhere you can see it. We measured whether it actually behaves like one for comparison and dict lookups. It doesn't -- and that silently breaks lookups when an ID crosses a string boundary, like a URL path or a JSON payload.

1. A UUID object never equals its own string form

import uuid

u = uuid.uuid4()
print(u)
print(type(u))
print(u == str(u))
31e81eb9-5d5d-410a-ab92-7f0ffa9a2d3d
<class 'uuid.UUID'>
False

Measured: uuid.uuid4() returns a UUID object, not a string. Printing it shows the same text you'd expect from the string form, but == against that exact string returns False -- UUID.__eq__ only considers another UUID equal, never a plain string, no matter how it's formatted.

2. This silently breaks dict/set lookups across a string boundary

import uuid

u = uuid.uuid4()
sessions = {u: "session data"}

received_id = str(u)   # e.g. came in as a URL path segment or JSON field
print(received_id in sessions)
print(u in sessions)
False
True

Measured: the same dict entry is found when looked up by the original UUID object, but in (and a direct sessions[received_id], which would raise KeyError) returns False for the string that looks identical when printed. This is exactly the shape of a real request-handling bug: an ID generated and stored as a UUID object, then received back later as a plain string from a URL parameter, HTTP header, or JSON body -- the lookup fails with no type error and no obviously wrong value to spot, just a silent miss.

3. The fix: normalize to UUID before comparing or keying

import uuid

u = uuid.uuid4()
sessions = {u: "session data"}
received_id = str(u)

normalized = uuid.UUID(received_id)
print(normalized == u)
print(normalized in sessions)
True
True

Measured: parsing the received string back into a uuid.UUID object before comparing or looking it up resolves correctly -- UUID.__eq__ and hashing both work as expected between two UUID objects, including when one came from a different-case string (uuid.UUID normalizes case on parse). The fix is consistency: pick one representation (object or string) for anything used as a cache/dict key or compared with ==, and convert at the boundary every time an ID crosses in or out as text.

Frequently asked questions

Is a Python uuid.UUID object equal to its string representation?

No -- we measured uuid.uuid4() == str(uuid.uuid4()) returning False, even on the exact same UUID. UUID.__eq__ only compares against other UUID objects, never strings.

Why does a dict lookup by UUID string fail in Python?

Because the dict key is a UUID object, not a string, and the two don't hash or compare equal. We measured a dict entry keyed by a UUID object failing an 'in' check against the identical-looking string, while the original UUID object still matched.

How do I fix UUID vs string comparison problems in Python?

Parse the string back into a UUID object with uuid.UUID(s) before comparing or using it as a key. We confirmed this normalized form correctly equals and hashes the same as the original UUID object.

Does uuid.UUID() care about uppercase vs lowercase hex digits?

No -- we measured uuid.UUID() on an uppercased UUID string comparing equal to the original lowercase UUID object, since UUID parsing normalizes case; the str-vs-UUID type mismatch is the real issue, not case.

Can a KeyError from a UUID lookup happen even though the ID 'matches'?

Yes -- we measured this exact scenario: looking up a dict keyed by a UUID object using the equivalent string raises a miss (KeyError on direct indexing, False via 'in'), even though printing both the key and the lookup value shows identical text.

Written with AI assistance. Measured on 2026-10-04 with Python 3.13.5's built-in uuid module: generated a UUID object with uuid.uuid4(), used it as a dict key, then looked the same entry up by its string form (str(u)) and by a fresh uuid.UUID() object parsed from that string. PlainHeaders is our extension; Python's uuid module is built in.