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

Для чего полезен __path__?

Я никогда не замечал атрибут __path__, который определяется на некоторых моих пакетах до сегодняшнего дня. Согласно документации:

Пакеты поддерживают еще одну специальную атрибут __path__. Это инициализируется как список, содержащий имя каталога, в котором находится пакеты __init__.py перед кодом в этом файле. Эта переменная может быть изменена; делать это влияет на будущие поиски модулей и подпакеты, содержащиеся в пакет.

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

Может кто-нибудь объяснить мне, что именно это означает и почему я когда-нибудь захочу его использовать?

4b9b3361

Ответ 1

Обычно это используется с pkgutil, чтобы пакет был размещен на диске. Например, zope.interface и zope.schema являются отдельными дистрибутивами (zope является "пакетом пространства имен" ). У вас может быть zope.interface, установленный в /usr/lib/python2.6/site-packages/zope/interface/, тогда как вы используете zope.schema локально в /home/me/src/myproject/lib/python2.6/site-packages/zope/schema.

Если вы поместите pkgutil.extend_path(__path__, __name__) в /usr/lib/python2.6/site-packages/zope/__init__.py, то оба zope.interface и zope.schema будут импортированы, потому что pkgutil будет иметь изменение __path__ - ['/usr/lib/python2.6/site-packages/zope', '/home/me/src/myproject/lib/python2.6/site-packages/zope'].

pkg_resources.declare_namespace (часть Setuptools) похожа на pkgutil.extend_path, но больше знает об zips на пути.

Вручную изменение __path__ является необычным и, вероятно, не является необходимым, хотя полезно учитывать переменную при отладке проблем импорта с пакетами пространства имен.

Вы также можете использовать __path__ для monkeypatching, например, у меня есть обезьяна, переданная через distutils, создавая файл distutils/__init__.py, который находится на раннем этапе sys.path:

import os
stdlib_dir = os.path.dirname(os.__file__)
real_distutils_path = os.path.join(stdlib_dir, 'distutils')
__path__.append(real_distutils_path)
execfile(os.path.join(real_distutils_path, '__init__.py'))
# and then apply some monkeypatching here...

Ответ 2

Если вы измените __path__, вы можете заставить интерпретатор искать в другом каталоге для модулей, принадлежащих этому пакету.

Это позволит вам, например, загружать разные версии одного и того же модуля в зависимости от условий выполнения. Вы можете сделать это, если хотите использовать разные реализации одинаковой функциональности на разных платформах.

Ответ 3

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

Рассмотрим следующее.

  • У меня есть два пакета mypkg и _mypkg_foo.
  • _mypkg_foo содержит дополнительный модуль для mypkg, foo.py.
  • как загружено и установлено, mypkg не содержит foo.py.

mypkg __init__.py может сделать что-то вроде этого:

try:
    import _mypkg_foo
    __path__.append(os.path.abspath(os.path.dirname(_mypkg_foo.__file__)))
    import mypkg.foo
except ImportError:
    pass

Если кто-то установил пакет _mypkg_foo, то mypkg.foo доступен для них. Если они этого не сделали, его не существует.

Ответ 4

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

Например, у меня есть пакет под названием views, который собирал ряд вспомогательных служебных функций, которые запутывались с главной целью пакета верхнего уровня. Мне удалось переместить эти вспомогательные функции в подкаталог utils и добавить следующую строку в __init__.py для пакета views:

__path__.append(os.path.join(os.path.dirname(__file__), "utils"))

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

(Я попытался сделать что-то подобное с операторами import в файле views/__init__.py, но модули подпакетов все еще не были видны через импорт пакета view - я не совсем уверен, что если я "Там что-то не хватает, комментарии об этом приветствуются!)

(Этот ответ основан на установке Python 2.7)