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

Какова цель атрибута "__package__" в Python?

Все, что я хочу знать, , что именно означает __package__? Не нашел никаких объяснений в официальном документе, даже на SO.

Если бы вы могли привести несколько примеров, я был бы очень доволен.

4b9b3361

Ответ 1

См. PEP 366 и справочная документация по системе импорта

Основное предлагаемое изменение - введение нового атрибута уровня модуля, __package__. Когда он присутствует, относительный импорт будет основываться на этом атрибуте, а не на атрибуте __name__.

и

  • Необходимо установить атрибут __package__. Его значение должно быть строкой, но оно может быть тем же значением, что и его __name__. Если для атрибута установлено значение None или отсутствует, система импорта заполнит его более подходящим значением. Когда модуль является пакетом, его значение __package__ должно быть установлено на его __name__. Если модуль не является пакетом, __package__ должен быть установлен в пустую строку для модулей верхнего уровня или для подмодулей в имя родительских пакетов. Подробнее см. PEP 366.

Итак, для модуля, расположенного в foo/bar/baz.py, __name__ установлено значение foo.bar.baz, а __package__ установлено на foo.bar, а foo/bar/__init__.py будет иметь foo.bar для __name__ и __package__.

Ответ 2

  Все, что я хочу знать, это то, что именно означает __package__

Это механизм, который разрешает явный относительный импорт.

Существует три возможных категории значений для __package__

  • Имя пакета (строка)
  • Пустая строка
  • Без

Название пакета

То есть, если модуль находится в пакете, для __package__ задается имя пакета, чтобы разрешить явный относительный импорт. частности:

Когда модуль представляет собой пакет, его значение __package__ должно быть установлено равным __name__. Если модуль не является пакетом, для субмодулей __package__ следует установить [...] имя родительского пакета.

Пустая строка

Если модуль находится в корневом или верхнем уровне, то есть текущий модуль импортируется с помощью

import current_module

или когда модуль верхнего уровня запускается как точка входа, например:

$ python -m current_module

затем __package__ - пустая строка. Или, как сказано в документации:

Когда модуль не является пакетом, для модулей верхнего уровня __package__ следует установить пустую строку...

Без

Если модуль/скрипт запускается по имени файла, __package__ - Нет:

Если основной модуль указан в имени файла, для атрибута __package__ будет установлено значение Нет.

Доказательства

Во-первых, давайте создадим файловую структуру с шумной отладкой - используя Python 3.6:

text = "print(f'{__name__}, __file__: {__file__}, __package__: {repr(__package__)}')"

from pathlib import Path
Path('foo.py').write_text(text)
Path('package').mkdir()
Path('package/__init__.py').write_text(text)
Path('package/__main__.py').write_text(text)
Path('package/bar.py').write_text(text)

# and include a submodule with a relative import:
Path('package/baz.py').write_text(text + '\nfrom . import bar')

Теперь мы видим, что foo.py, выполняемый как модуль, имеет пустую строку для __package__, тогда как скрипт, выполняемый по имени файла в качестве точки входа, имеет None:

$ python -m foo
__main__, __file__: ~\foo.py, __package__: ''
$ python foo.py
__main__, __file__: foo.py, __package__: None

Когда мы выполняем пакет как модуль для точки входа, запускается его модуль __init__.py, затем запускается его __main__.py:

$ python -m package
package, __file__: ~\package\__init__.py, __package__: 'package'
__main__, __file__: ~\package\__main__.py, __package__: 'package'

Аналогичным образом, когда мы выполняем подмодуль в качестве модуля для точки входа, модуль __init__.py запускается, а затем запускается:

$ python -m package.bar
package, __file__: ~\package\__init__.py, __package__: 'package'
__main__, __file__: ~\package\bar.py, __package__: 'package'

Наконец, мы видим, что явный относительный импорт, вся причина наличия __package__ (что здесь происходит в последнюю очередь), включен:

$ python -m package.baz
package, __file__: ~\package\__init__.py, __package__: 'package'
__main__, __file__: ~\package\baz.py, __package__: 'package'
package.bar, __file__: ~\package\bar.py, __package__: 'package'

Обратите внимание, что в выводе я заменил ~ на родительские каталоги.