Оператор import в Python — способы импортирования
В этом уроке вы изучите возможности оператора импорта в Python и то, как они работают «под капотом».
- Импорт модулей в Python
- Импорт объекта
- Импорт объекта из модуля как object_alias
- Конструкция from module import *
- Заблуждение об операторе import в Python
Импорт модулей в Python
Когда вы импортируете модуль, Python делает две вещи:
- Сначала проверяет, был ли модуль загружен и кэширован в sys.modules. Если нет, он выполнит модуль и создаст ссылку на объект модуля.
- Во-вторых, добавляет имя модуля в глобальное пространство имен, ссылающееся на тот же объект модуля.
Следующая программа импортирует модуль math и распечатывает математический объект math в sys.modules и глобальном пространстве имен:
import sys import math print('sys.modules:', hex(id(sys.modules['math']))) if 'math' in globals(): print('globals: ', hex(id(globals()['math'])))
Выход:
sys.modules: 0x20456766590 globals: 0x20456766590
Как видите, математическая переменная ссылается на один и тот же объект модуля.
Если вы импортируете math во второй раз, Python не выполнит модуль повторно, а получит его из кэша sys.modules.
Импорт объекта
Когда вы импортируете объект (функцию, класс и т. д.) из модуля в Python, необходимо сделать следующее:
- Сначала проверьте, был ли модуль загружен и кэширован в sys.modules. Если нет, он выполнит модуль и создаст ссылку на объект модуля.
- Во-вторых, добавьте объект импорта в глобальное пространство имен.
В этом случае Python добавляет в глобальное пространство имен не переменную, ссылающуюся на модуль, а переменную, ссылающуюся на импортируемый объект.
В следующем примере функция ceil импортируется из объекта math:
import sys from pprint import pprint from math import ceil print('sys.modules:', hex(id(sys.modules['math']))) pprint(globals())
Выход:
sys.modules: 0x11d659c2130 {'__annotations__': {}, '__builtins__': <module 'builtins'(built-in)>, '__cached__': None, '__doc__': None, '__file__': 'C:/oop/app.py', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000011D65A008E0>, '__name__': '__main__', '__package__': None, '__spec__': None, 'ceil': <built-in function ceil>, 'pprint': <function pprint at 0x0000011D661C4040>, 'sys': <module 'sys'(built-in)>}
В этом примере Python загружает математический модуль в sys.modules. Однако он создает ссылку только на функцию ceil, а не на объект модуля math в глобальном пространстве имен.
Импорт объекта из модуля как object_alias
Когда вы загружаете объект из модуля и используете псевдоним в Python, сделайте следующее:
- Сначала проверьте, был ли модуль загружен и кэширован в sys.modules. Если нет, он выполнит модуль и создаст ссылку на объект модуля.
- Во-вторых, создайте псевдоним, ссылающийся на импортируемый объект, и добавьте его в глобальное пространство имен.
Следующий пример импортирует функцию ceil из модуля math и использует псевдоним:
import sys from math import ceil as ceiling print('sys.modules:', hex(id(sys.modules['math']))) print('globals:', hex(id(globals()['ceiling'])))
Выход:
sys.modules: 0x1cc4f244ae0 {'__annotations__': {}, '__builtins__': <module 'builtins'(built-in)>, '__cached__': None, '__doc__': None, '__file__': 'C:/oop/app.py', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CC4EA708E0>, '__name__': '__main__', '__package__': None, '__spec__': None, 'ceiling': <built-in function ceil>, 'pprint': <function pprint at 0x000001CC4F234040>, 'sys': <module 'sys'(built-in)>}
Конструкция from module import *
Когда вы импортируете все из модуля, Python сделает следующее:
- Проверит, был ли модуль загружен и кэширован в sys.modules. Если нет, он выполнит модуль и создаст ссылку на объект модуля.
- Во-вторых, добавит все символы из модуля в глобальное пространство имен.
Например, следующий код импортирует все объекты из модуля math:
import sys from pprint import pprint from math import * print('sys.modules:', hex(id(sys.modules['math']))) pprint(globals())
Выход:
sys.modules: 0x1e1ebf24b30 {'__annotations__': {}, '__builtins__': <module 'builtins'(built-in)>, '__cached__': None, '__doc__': None, '__file__': 'C:/oop/app.py', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001E1EB7408E0>, '__name__': '__main__', '__package__': None, '__spec__': None, 'acos': <built-in function acos>, 'acosh': <built-in function acosh>, 'asin': <built-in function asin>, .... 'tau': 6.283185307179586, 'trunc': <built-in function trunc>}
Как ясно видно из вывода, Python добавляет все функции из модуля math в глобальные пространства имен. В этом случае, если в глобальном пространстве имен существуют какие-либо символы, Python заменит их ссылки.
Это часто приводит к ошибкам, которые трудно отследить.
Заблуждение об операторе import в Python
Одним из наиболее распространенных заблуждений относительно оператора импорта является то, что многие считают следующее утверждение:
from math import ceil
более эффективен, чем:
import math
Потому что первый оператор импортирует только функцию ячейки, а второй оператор импортирует весь математический модуль.
Однако в обоих случаях Python загружает весь модуль math.
Первый оператор создает символ, ссылающийся на функцию ceil из math-модуля, а второй оператор создает математический символ, ссылающийся на объект математического модуля.