При импортировании, модуль выполняется как программа от первой до последней строки. При этом создается объект типа ModuleType
, который сохраняется по исходному имени в кеше sys.modules
. При любом повторном импортировании модуль уже не выполняется при загрузке, а извлекается в готовом виде из кеша. Объекты, которые в коде модуля рассматриваются как глобальные, хранятся в его свойстве __dict__
. Функции модуля, которые обращаются к глобальным переменным, ищут их именно там. А чтобы сохранить мобильность функции как самостоятельного объекта, связь с пространством имен модуля поддерживается через свойство функции __globals__
. Это свойство указывает на ранее упомянутый словарь __dict__
исходного модуля.
Что происходит в примере из вопроса? Импортируя функцию строкой from my_library import a
, вы загружаете весь модуль. При этом функция будет доступна из вашей программы по имени a
, модуль сохранится как sys.modules['my_library']
, а пространство глобальных имен функции будет хранится в словаре, который будет доступен по трем именам:
sys.modules['my_library'].__dict__
sys.modules['my_library'].__dict__['a'].__globals__ # он же a.__globals__
sys.modules['my_library'].__dict__['b'].__globals__
Функция a
во время работы будет искать функцию b
в словаре a.__globals__
, а не в текущем пространстве глобальных имен. Если в пределах модуля функция b
не определена, то при попытке выполнить a()
возникнет ошибка NameError: name 'b' is not defined
. Вы можете определить b
за пределами модуля, но a
не сможет её увидеть, пока в словаре a.__globals__
не появится ключ "b"
, указывающий на вашу функцию.
P.S. Хорошее, несмотря на возраст, видео на тему импорта и работы с модулями: David Beazley - Modules and Packages: Live and Let Die! - PyCon 2015