Если a = 4567, b = a, a += 1, чему равно b и почему в Python
Что проверяют
Понимаете ли вы модель имён и объектов в Python: переменная это ярлык на объект, а не ячейка со значением. Число 4567 выбрано нарочно, чтобы увести разговор от кэша малых чисел. Следом почти всегда дают тот же пример со списком и +=.
Как отвечать
Ответ: b == 4567, а a == 4568. Объяснение по шагам:
a = 4567: создаётся объектintсо значением 4567, имяaна него ссылается;b = a: копирования не происходит, имяbпривязывается к тому же объекту. В этот моментa is bдаётTrue;a += 1: уintнет метода__iadd__, поэтому Python выполняетa = a + 1. Вызывается__add__, он возвращает новый объект 4568, и имяaпереносится на него. Объект 4567 не меняется, потому чтоintнеизменяемый, иbпродолжает на него указывать.
Теперь контрпример, ради которого вопрос и задают:
a = [4567]
b = a
a += [1]
print(b) # [4567, 1]У списка __iadd__ есть, он работает как extend и меняет объект на месте. Имя a остаётся на том же списке, и изменение видно через b. А вот a = a + [1] создал бы новый список и b не тронул бы.
Про кэш хватит одной фразы: CPython заранее создаёт объекты для чисел от минус 5 до 256, поэтому is для маленьких чисел может давать True там, где для больших даёт False. На ответ в этой задаче кэш не влияет, это деталь реализации, а числа сравнивают через ==.
Где спотыкаются
Говорят, что b «скопировало значение» в момент присваивания. Результат совпадает, но объяснение неверное и ломается на первом же примере со списком.
Считают, что += всегда изменяет объект на месте. Для неизменяемых типов (int, str, tuple) это новое присваивание, для изменяемых вызов __iadd__.
Связывают ответ с кэшем малых чисел и начинают рассуждать про id. Будь там a = 5, b всё равно осталось бы равным 5: кэш влияет на результат is, а не на то, куда смотрит имя.