В чем разница между алгебраическим сложением и конкатенацией строк в Python?
Что проверяют
Знаете ли вы, что в Python поведение + определяет тип операндов, и понимаете ли строгую типизацию. Следом обычно просят объяснить, почему склеивать строки в цикле через + плохо и чем это заменить.
Как отвечать
Оператор один, а работу делают разные методы: a + b превращается в a.__add__(b), а если тот вернул NotImplemented, Python пробует b.__radd__(a). Отсюда разница:
- сложение чисел:
int,float,Decimal,Fractionвозвращают сумму, смешанные типы приводятся к более широкому:1 + 2.0даёт3.0.intне переполняется, уfloatдвоичная точность, поэтому0.1 + 0.2 != 0.3.boolэто подклассint, иTrue + Trueравно2; - конкатенация строк:
str + strсоздаёт новую строку, исходные не меняются, строки неизменяемы. Операция не коммутативна; - смешение типов:
"1" + 1падает сTypeError, неявного приведения, как в JavaScript, нет. Приводят явно:str(1)илиint("1").
Про производительность: каждый + копирует строки, и сборка результата из n кусков в цикле в худшем случае квадратична. CPython умеет расширять строку на месте при s += t, если на неё нет других ссылок, но это деталь реализации, и PEP 8 советует на неё не рассчитывать. Правильные инструменты: "".join(parts) для списка, f-строки для шаблонов, io.StringIO для потоковой сборки. Встроенная sum() строки не складывает вовсе и сама подсказывает join в тексте ошибки.
Где спотыкаются
Думают, что Python сам приведёт число к строке, и удивляются TypeError на "Итого: " + total.
Переносят поведение строк на списки. Для списка a += [x] меняет тот же объект на месте, как extend, и изменение видно по всем ссылкам, а a = a + [x] создаёт новый список. Для строк оба варианта дают новый объект.
Сравнивают сумму дробных чисел через ==. Для денег нужен Decimal, для сравнения с допуском math.isclose.