Как работает позднее связывание в Python?
Чтобы такого не происходило можно сделать так:def multipliers():
  return [lambda x, i=i: i * x for i in range(4)]
    
print([m(2) for m in multipliers()]) # [0, 2, 4, 6]
или такdef multipliers():
  return (lambda x: i * x for i in range(4))
    
print([m(2) for m in multipliers()]) # [0, 2, 4, 6]
Второй вариант в данном случае мне нравится больше.
Но первый - универсальное решение для всех подобных проблем с лямбдой.
Тоже долго ломал голову над этим примером и даже предыдущий ответ не сразу помог мне осознать как всё работает. Дело в том, что i воспринимается как глобальная переменная по отношению к создаваемой лямбда функции, т.е. в лямбде фигурирует не значение i, а ссылка на неё.
После того, как созданы все лямбды,  i равна 3. Вот и получаем массив одинаковых значений.