Identity, equality and copies
== asks whether two objects have equal values. is asks whether two names refer to the very same object, the one id(x) identifies. Use is for None and other singletons, and == for everything else. Small ints and short strings are sometimes shared, so is can seem to work on them; never rely on it.
import copy
a = [1, 2]
b = [1, 2]
print(a == b, a is b) # True False
t = (a, "x")
a.append(3)
print(t) # the list inside changed
shared = [a, a]
deep = copy.deepcopy(shared)
print(deep[0] is deep[1]) # TrueMutable and immutable
Lists, dicts, sets and most objects can change in place, and every name bound to one sees the change. Numbers, strings, tuples and frozensets cannot. A tuple is only as fixed as what it holds: a tuple containing a list can still change, so it cannot be hashed.
x += y changes a list in place but builds a new object for a tuple or a string. A function that receives a list receives that same list, not a copy.
copy.deepcopy copies nested objects all the way down and keeps shared references shared, so the copy has the same shape as the original.
Your task
Write unique_objects(items) dropping repeats of the same object, by identity rather than equality, keeping the first. Write with_item(order, item) returning a new order dict whose "items" list ends with item, leaving order and the list inside it unchanged. Write freeze(data) returning a hashable copy of nested data: lists and tuples become tuples, sets become frozensets, and dicts become tuples of (key, value) pairs sorted by key, all recursively.
The explanation opens here once every check passes.