От чего зависит выбор глобального имени функцией, если её вызвали из другого модуля?
Функция ищет глобальное имя в пространстве имён модуля, где она была определена, а не в модуле, из которого её вызвали. Вызов функции из другого модуля не переносит её глобальный контекст.
Такое поведение связано с моделью пространств имён и лексической областью видимости Python. Функция сохраняет связь с окружением определения, чтобы её поведение не зависело от случайного места вызова и оставалось предсказуемым.
Модули изолируют свои глобальные имена друг от друга. Это предотвращает неявное влияние переменных вызывающего кода на внутреннюю логику импортированной функции.
Предположим, функция из модуля service обращается к имени mode, а другой модуль runner также содержит собственное mode. Ошибка в рассуждении возникает, если ожидать, что функция использует значение из runner только потому, что вызвана именно там.
Неверное понимание приводит к скрытым зависимостям, неожиданному поведению при тестировании и ошибкам после импорта. Особенно опасно полагаться на случайное наличие одинаковых имён в разных модулях.
У функции есть ссылка на глобальное пространство имён, связанное с местом её определения. Для обычной функции поиск имени идёт среди локальных имён, затем в замыкающих областях, затем в глобальном пространстве модуля-определения и, наконец, среди встроенных имён.
Модуль, из которого выполняется вызов, в этот поиск не включается. Технически функция использует словарь глобальных имён, доступный через её атрибут __globals__; при обычном определении это словарь модуля, содержащего функцию.
Функция read создана в пространстве имён module_a, поэтому находит marker со значением A. Передача самой функции в module_b не меняет её глобальное пространство имён.
Если функции действительно нужно использовать настройки вызывающего кода, зависимость следует передавать явно: аргументом, объектом конфигурации или зависимым сервисом. Альтернатива — обращаться к атрибуту конкретного модуля, например config.mode; это делает источник значения явным, но усиливает связанность с модулем конфигурации.
В библиотечном модуле функция форматирования использовала глобальное имя locale. Тестовый модуль временно присваивал собственное locale перед вызовом и ожидал, что форматирование переключится, но функция продолжала видеть значение из модуля библиотеки.
Рассматривались два варианта. Изменять глобальную переменную библиотеки во время теста можно быстро, но это создаёт состояние, общее для всех тестов, и делает их зависимыми от порядка выполнения. Передавать локаль параметром немного многословнее, зато зависимость становится явной и безопасной для параллельного запуска.
Было выбрано явное добавление параметра с разумным значением по умолчанию. В результате функция сохранила удобный обычный вызов, а тесты получили возможность надёжно задавать локаль без подмены чужого пространства имён.
Меняется ли глобальный контекст функции после присваивания ей другого имени?
Нет. Переименование или передача функции в другую переменную меняет только ссылку на объект функции, но не её __globals__. Контекст изменится лишь при создании новой функции с другим глобальным пространством имён, например через специальное выполнение исходного кода.
Что произойдёт, если глобальное имя изменить после определения функции?
При следующем обращении функция увидит новое значение в глобальном словаре своего модуля. Связь сохраняется не с конкретным значением, а с пространством имён, поэтому изменение или удаление записи влияет на последующие вызовы.
Можно ли заставить функцию использовать переменную вызывающего модуля без изменения её исходного кода?
Надёжный штатный способ — нет: вызов не передаёт функции глобальное пространство имён вызывающего кода. Можно изменить объект, содержащий её глобальные имена, но это хрупкий и опасный приём: он затрагивает все вызовы функции и может нарушить работу других частей программы. Предпочтительнее передавать нужное значение явно.