You can also use a recursive function for a no-import solution:
def combo(data, c=[]):
if not data:
yield c
else:
for i in data[0]:
yield from combo(data[1:], c+[i])
d = {'variable_1': ['a1', 'b1', 'c1'], 'variable_2': ['a2', 'b2', 'c2'], 'variable_3': ['a3', 'b3', 'c3']}
keys, values = zip(*d.items())
result = [dict(zip(keys, i)) for i in combo(values)]
Output:
[{'variable_1': 'a1', 'variable_2': 'a2', 'variable_3': 'a3'}, {'variable_1': 'a1', 'variable_2': 'a2', 'variable_3': 'b3'}, {'variable_1': 'a1', 'variable_2': 'a2', 'variable_3': 'c3'}, {'variable_1': 'a1', 'variable_2': 'b2', 'variable_3': 'a3'}, {'variable_1': 'a1', 'variable_2': 'b2', 'variable_3': 'b3'}, {'variable_1': 'a1', 'variable_2': 'b2', 'variable_3': 'c3'}, {'variable_1': 'a1', 'variable_2': 'c2', 'variable_3': 'a3'}, {'variable_1': 'a1', 'variable_2': 'c2', 'variable_3': 'b3'}, {'variable_1': 'a1', 'variable_2': 'c2', 'variable_3': 'c3'}, {'variable_1': 'b1', 'variable_2': 'a2', 'variable_3': 'a3'}, {'variable_1': 'b1', 'variable_2': 'a2', 'variable_3': 'b3'}, {'variable_1': 'b1', 'variable_2': 'a2', 'variable_3': 'c3'}, {'variable_1': 'b1', 'variable_2': 'b2', 'variable_3': 'a3'}, {'variable_1': 'b1', 'variable_2': 'b2', 'variable_3': 'b3'}, {'variable_1': 'b1', 'variable_2': 'b2', 'variable_3': 'c3'}, {'variable_1': 'b1', 'variable_2': 'c2', 'variable_3': 'a3'}, {'variable_1': 'b1', 'variable_2': 'c2', 'variable_3': 'b3'}, {'variable_1': 'b1', 'variable_2': 'c2', 'variable_3': 'c3'}, {'variable_1': 'c1', 'variable_2': 'a2', 'variable_3': 'a3'}, {'variable_1': 'c1', 'variable_2': 'a2', 'variable_3': 'b3'}, {'variable_1': 'c1', 'variable_2': 'a2', 'variable_3': 'c3'}, {'variable_1': 'c1', 'variable_2': 'b2', 'variable_3': 'a3'}, {'variable_1': 'c1', 'variable_2': 'b2', 'variable_3': 'b3'}, {'variable_1': 'c1', 'variable_2': 'b2', 'variable_3': 'c3'}, {'variable_1': 'c1', 'variable_2': 'c2', 'variable_3': 'a3'}, {'variable_1': 'c1', 'variable_2': 'c2', 'variable_3': 'b3'}, {'variable_1': 'c1', 'variable_2': 'c2', 'variable_3': 'c3'}]