Подтвердить что ты не робот

Что происходит при импорте пакета?

Для эффективности я пытаюсь выяснить, как работает python с его кучей объектов (и системой пространств имен, но это более или менее понятно). Итак, в основном, я пытаюсь понять, когда объекты загружаются в кучу, сколько их есть, сколько они живут и т.д.

И мой вопрос , когда я работаю с пакетом и импортирую что-то из него:

from pypackage import pymodule

какие объекты загружаются в память (в кучу объекта интерпретатора python)? И в целом: что происходит?:)

Я думаю, что приведенный выше пример делает что-то вроде: в памяти был создан некоторый объект пакета pypackage (который содержит некоторую информацию о пакете, но не слишком много), модуль pymodule был загружен в память и его ссылка была создана в локальном пространстве имен. Здесь важно: в памяти не создавались другие модули pypackage (или других объектов), если только это не указано явно (в самом модуле или где-то в трюках инициализации пакета и перехватах, которые я не знаком с). В конце единственная важная вещь в памяти - pymodule (т.е. Все объекты, которые были созданы при импорте модуля). Это так? Буду признателен, если кто-нибудь прояснит этот вопрос. Может быть, вы могли бы посоветовать какую-нибудь полезную статью об этом? (документация охватывает более конкретные вещи)

Я нашел следующее к тому же вопросу об импорте модулей:

Когда Python импортирует модуль, он сначала проверяет реестр модулей (sys.modules), чтобы узнать, импортирован ли модуль. Если это так, Python использует существующий объект модуля как есть.

В противном случае Python делает что-то вроде этого:

  • Создайте новый пустой объект модуля (это, по существу, словарь)
  • Вставьте этот объект модуля в словарь sys.modules
  • Загрузите объект кода модуля (при необходимости сначала скомпилируйте модуль)
  • Выполнить объект кода модуля в пространстве имен новых модулей. Все переменные, назначенные кодом, будут доступны через объект модуля.

И был бы признателен за то же объяснение относительно пакетов.

Кстати, с пакетами имя модуля добавляется в sys.modules странно:

>>> import sys
>>> from pypacket import pymodule
>>> "pymodule" in sys.modules.keys()
False
>>> "pypacket" in sys.modules.keys()
True

А также есть практический вопрос, касающийся одного и того же вопроса.

Когда я создаю набор инструментов, которые могут использоваться в разных процессах и программах. И я поместил их в модули. У меня нет выбора, кроме как загрузить полный модуль, даже если я хочу использовать только одну функцию, объявленную там. Как я вижу, можно сделать эту проблему менее болезненной, создав небольшие модули и помещая их в пакет (если пакет не загружает все его модули при импорте только одного из них).

Есть ли лучший способ сделать такие библиотеки на Python? (С помощью простых функций, которые не имеют каких-либо зависимостей в их модуле.) Возможно ли с помощью C-расширений?

PS извините за такой длинный вопрос.

4b9b3361

Ответ 1

У вас здесь несколько разных вопросов.,.

Об импорте пакетов

При импорте пакета последовательность шагов такая же, как при импорте модуля. Единственное различие заключается в том, что код пакета (т.е. Код, который создает "объект кода модуля" ), является кодом пакета __init__.py.

Итак, да, подмодули пакета не загружаются, если только __init__.py не делает это явно. Если вы выполняете from package import module, загружается только module, если, конечно, он не импортирует другие модули из пакета.

sys.modules имена модулей, загружаемых из пакетов

При импорте модуля из пакета имя, которое добавлено в sys.modules, является "квалифицированным именем", которое указывает имя модуля вместе с именами разделенных точками любых пакетов, из которых вы его импортировали. Итак, если вы делаете from package.subpackage import mod, то, что добавлено в sys.modules, есть "package.subpackage.mod".

Импортирование только части модуля

Как правило, не очень важно импортировать весь модуль вместо одной функции. Вы говорите, что это "больно", но на практике это почти никогда не бывает.

Если, как вы говорите, функции не имеют внешних зависимостей, тогда они просто чистые Python, и загрузка их не займет много времени. Обычно при импорте модуля требуется много времени, потому что он загружает другие модули, что означает, что он имеет внешние зависимости, и вам нужно загрузить все это.

Если ваш модуль имеет дорогостоящие операции, которые происходят при импорте модуля (т.е. они являются глобальным модульным кодом, а не внутри функции), но они не являются существенными для использования всех функций в модуле, тогда вы можете, если вам нравится, перепроектируйте свой модуль, чтобы отложить загрузку до более позднего времени. То есть, если ваш модуль делает что-то вроде:

def simpleFunction():
    pass

# open files, read huge amounts of data, do slow stuff here

вы можете изменить его на

def simpleFunction():
    pass

def loadData():
    # open files, read huge amounts of data, do slow stuff here

а затем сообщите людям "вызов someModule.loadData(), когда вы хотите загрузить данные". Или, как вы предположили, вы можете поместить дорогостоящие части модуля в свой отдельный модуль внутри пакета.

Я никогда не предполагал, что импорт модуля вызывает значительную отдачу от производительности, если только модуль уже достаточно велик, чтобы его можно было разделить на более мелкие модули. Создание тонны крошечных модулей, каждая из которых содержит одну функцию, вряд ли принесет вам что-либо, кроме головных болей от необходимости отслеживать все эти файлы. У вас действительно есть конкретная ситуация, когда это имеет для вас значение?

Кроме того, в отношении вашего последнего момента, насколько мне известно, одна и та же стратегия загрузки "все или ничего" применяется к модулям расширения C, как к чистым модулям Python. Очевидно, что, как и в случае с модулями Python, вы можете разделить вещи на более мелкие модули расширения, но вы не можете сделать from someExtensionModule import someFunction, не запустив остальную часть кода, который был упакован как часть этого модуля расширения.

Ответ 2

Примерная последовательность шагов, возникающих при импорте модуля, выглядит следующим образом:

  • Python пытается найти модуль в sys.modules и ничего не делает, если он найден. Пакеты заполняются их полным именем, поэтому пока pymodule отсутствует в sys.modules, pypacket.pymodule будет (и может быть получен как sys.modules["pypacket.pymodule"].

  • Python находит файл, который реализует модуль. Если модуль является частью пакета, как определено синтаксисом x.y, он будет искать каталоги с именем x, которые содержат как __init__.py, так и y.py (или дополнительные подпакеты). В самом нижнем файле находится файл .py, файл .pyc или файл .so/.pyd. Если файл, который подходит для модуля, не найден, будет добавлен ImportError.

  • Создается пустой объект модуля, а код в модуле выполняется с модулем __dict__ в качестве пространства имен выполнения. 1

  • Объект модуля помещается в sys.modules и вводится в пространство импортеров.

Шаг 3 - это точка, в которой "объекты загружаются в память": рассматриваемые объекты являются объектом модуля и содержимым пространства имен, содержащегося в его __dict__. Этот dict обычно содержит функции и классы верхнего уровня, созданные как побочный эффект выполнения всех def, class и других операторов верхнего уровня, обычно содержащихся в каждом модуле.

Обратите внимание, что приведенное выше описывает только реализацию import по умолчанию. Существует несколько способов персонализации поведения импорта, например, путем переопределения встроенного __import__ или путем реализации импортировать крюки.


1 Если файл модуля является исходным файлом .py, он сначала будет скомпилирован в память, а объекты кода, являющиеся результатом компиляции, будут выполнены. Если это .pyc, объекты кода будут получены десериализацией содержимого файла. Если модуль является общей библиотекой .so или .pyd, он будет загружен с использованием средства загрузки разделяемой библиотеки операционной системы, а для инициализации модуля будет вызвана функция init<module> C.