Приведите пример выражения, объявленного один раз в рантайме и не меняющегося, но не являющегося константой
Что проверяют
Знаете ли вы, что констант на уровне языка в Python нет, и понимаете ли разницу между неизменяемым объектом и именем, которое просто никто не переприсваивает. Заодно проверяют, помните ли вы, когда именно вычисляется код: на импорте, при определении функции или при вызове.
Как отвечать
Скажите главное сразу: UPPER_CASE это соглашение, а Final из typing читает только статический анализатор, в рантайме присваивание пройдёт. Значит вопрос про объект, который создаётся один раз и дальше живёт неизменным.
Самый точный пример это значение аргумента по умолчанию: оно вычисляется один раз, в момент выполнения def, и хранится в __defaults__ функции.
def log(msg, started_at=datetime.now()):
...Значение started_at посчитано при импорте модуля, дальше не меняется, но константой не является: оно зависит от момента запуска, и его никто не защищает.
Рядом стоит назвать ещё пару случаев:
- значение, вычисленное на уровне модуля: тело модуля исполняется один раз, повторный импорт берёт готовый объект из
sys.modules; - результат под
functools.lru_cacheилиcached_property: посчитан по первому обращению и дальше отдаётся тот же объект; - члены
Enum: создаются при создании класса, переприсвоить их уже нельзя; - замкнутое значение в фабрике функций: снаружи до имени не добраться.
Где спотыкаются
Отвечают «кортеж» и на этом останавливаются. Кортеж неизменяем по составу, но имя можно переприсвоить следующей же строкой, а внутри кортежа может лежать список, который спокойно поменяют.
Второе: путают время вычисления. Аргумент по умолчанию считают вычисляемым при каждом вызове, отсюда и знаменитая ловушка с изменяемым значением по умолчанию: список в сигнатуре один на все вызовы и копит данные между ними.
Третье: заявляют, что Final или подчёркивание перед именем запрещают изменение. Ни то ни другое не запрещает в рантайме ничего: первое подсказка проверяющему типы, второе договорённость между людьми.