Я думаю, что, инвертировав пару значений ключа словаря, вы можете позже обратиться к возвращенному словарю удобно:
dd = dict()
for key, val in d.items():
for element in val:
if not dd.get(element):
dd[element] = list()
dd[element].append(key)
dd
# {1: ['o1'], 2: ['o1'], 3: ['o1'], 0: ['o2'], -1: ['o2'], 7: ['o3'], 8: ['o3'], 10: ['o3'], 11: ['o3']}
dd[1]
# ['o1']
Поскольку значения в исходном словаре не являются уникальными, вы хотите, чтобы значения нового словаря были списками.
Edit:
Для того, чтобы выровнять вывод с продуктом:
p = list(product (*d.values())
[tuple(dd[i] for i in val) for val in p]
# [(['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3']), (['o1'], ['o2'], ['o3'])]
Вы можете использовать ту же индексацию, чтобы получить пару ключ-значение