Базовая структура пакета Python
Last updated
Was this helpful?
Last updated
Was this helpful?
Для начала я объясню базовую структуру пакета Python, который обеспечивает каркас, на который вы можете добавить функции, которые мы обсудим позже в этих заметках (документация, автоматическое тестирование и т. д.). Один из способов узнать о структуре пакетов - просмотреть пакеты Python на GitHub, например, на на GitHub. Но поскольку в этих пакетах реализовано множество сложных функций обслуживания, и они могут использовать файлы, специфичные для интеграции с GitHub, это может быть запутанным способом начать изучение структуры пакетов Python (однако, как только вы узнаете основы, просмотр других пакетов будет отличным способ обнаружить новые функции, которые вы, возможно, захотите использовать в своем собственном пакете; в общем, вы можете многому научиться, читая код других людей).
Точно так же существует множество генераторов пакетов, фрагментов кода, которые будут генерировать скелетные пакеты, которые вы можете заполнить, чтобы создать свой собственный пакет. Популярным классом таких шаблонов пакетов являются шаблоны, созданные с помощью утилиты командной строки , которая позволяет вам создавать скелеты пакетов для многих различных языков/макетов, просто вызывая cookiecutter
с URL-адресом шаблона. Например, astropy
предоставляет cookiecutter, специфичный для пакетов в экосистеме astropy
. В общем, я избегаю использования таких шаблонов, особенно для начинающих упаковщиков, потому что эти шаблоны содержат сбивающее с толку количество дополнительных функций, которые затемняют базовую структуру пакета и отвлекают от базовой разработки пакета (когда я создаю шаблон с использованием шаблона astropy, даже не знаю, где начать код!). Шаблоны cookiecutter полезны для опытных пользователей, создающих множество пакетов, но я думаю, что с целью изучения упаковки лучше создать пакет с нуля и позже добавить каждую расширенную функцию по отдельности.
Первое решение, которое вы должны принять при создании пакета, - как назвать его. Поскольку переименовывать пакет позже раздражает, это важное решение, которое нужно принять на раннем этапе, и стоит потратить хотя бы несколько минут на размышления о хорошем имени. Запоминающееся, запоминающееся название поможет привлечь внимание к вашей посылке. В самом деле, я очень рад, что получил название «galpy», когда оно было доступно, и даже тогда я закончил только «galpy», потому что имя, которое я изначально хотел использовать, было pygd (от «галактической динамики Python»), но это имя уже было занято проектом на .
Кроме того, чтобы имя было запоминающимся, важно, чтобы оно было уникальным, поэтому вам нужно убедиться, что это имя еще не используется другим проектом. Для пакетов Python важно убедиться, что в (PyPI) нет пакета с тем именем, о котором вы думаете, путем поиска в их базе данных. Это важно, потому что в конечном итоге вы захотите установить свой пакет с помощью простого пакета pip install PACKAGE_NAME
, потому что это первое, что попробуют пользователи, когда узнают, что им нужно использовать ваш пакет. Если pip install PACKAGE_NAME
устанавливает другой пакет, многие пользователи в конечном итоге очень запутаются. Таким образом, хотя имя PyPI может отличаться от имени вашего пакета, в случае конфликта лучше отойти от предполагаемого имени пакета и выбрать тот, который доступен. Для пакета Python доступность на PyPI является наиболее важным соображением, но вы также можете проверить , чтобы в более общем плане проверить имена проектов с открытым исходным кодом (не обязательно в Python) и выполнить поиск на GitHub (хотя в случае GitHub, наиболее важные конфликты будут с пакетами, которые на самом деле используются более широким сообществом). Чтобы гарантировать, что имя не исчезнет во время разработки, вы можете как можно скорее зарегистрировать свой пакет в PyPI, опубликовав .
Что касается того, что выбрать в качестве имени, вкусы разнятся. Многие пакеты Python заканчиваются на py
, чтобы было ясно, что они являются пакетами Python (например, numpy
, scipy
, astropy
, galpy
), но это не правило, и имя пакета может быть любым (действительно, количество хороших, количество доступных имен, оканчивающихся на «ру», быстро сокращается). Вы можете выбрать имя, кратко описывающее то, что делает ваш пакет (это долгое время было моим предпочтительным соглашением об именах, что привело к таким сухим именам пакетов, как apogee
, mwdust
, gaia_tools
), или вы можете выбрать умное имя или аббревиатуру (мои собственные набеги на это направление wendy
и kimmy
, хотя никто, кажется, никогда не понимает их…; также иллюстрирует, что вы можете просто закончить на «у»!). Но я бы рекомендовал, чтобы имя вашего пакета было относительно коротким, потому что даже в эпоху завершения табуляции люди, использующие ваш код, в конечном итоге будут часто вводить его имя.
Таким образом, нам не придется утомительно ссылаться на PACKAGE_NAME
как на имя нашего незавершенного пакета, с этого момента мы будем использовать пакет exampy
в качестве примера (получить?). В этих заметках я буду использовать exampy
, чтобы проиллюстрировать все, что обсуждается. Пакет exampy
доступен здесь, на , и здесь, в .
После того, как вы определились с именем, пора приступить к созданию вашего пакета. Создайте каталог, в котором будет храниться ваш пакет, я обычно даю ему имя пакета, но это не обязательно. Позже мы разместим весь этот каталог на GitHub, и я буду называть его «каталог верхнего уровня». В этом каталоге верхнего уровня ваш пакет будет содержаться в подкаталоге с именем вашего пакета, в нашем примере это exampy/
. Этот каталог будет содержать весь ваш код. Другие подкаталоги каталога верхнего уровня будут содержать документацию, а также тесты и подкаталоги, которые также будут автоматически созданы при сборке и распространении кода (подробнее об этом позже). Мы будем использовать этот пример пакета на протяжении всей остальной части этих заметок, чтобы проиллюстрировать документацию и инструменты тестирования, поэтому вы можете продолжить и реализовать этот простой пакет самостоятельно, чтобы иметь возможность продолжать использовать его в следующих главах. Вы можете добавить его в GitHub как exampy-GITHUBUSERNAME
, чтобы отличать его от .
Файлы в каталоге верхнего уровня в основном содержат метаинформацию о вашем пакете. В каталоге верхнего уровня должен быть файл README с базовой информацией о пакете, он будет содержать , в конечном итоге он будет содержать файлы конфигурации для автоматического создания документации и для непрерывной интеграции тестов (но еще не!), Если вы размещаете пакет на GitHub может содержать один или несколько файлов, специфичных для интеграции с GitHub, и он будет содержать несколько файлов, связанных с установкой и распространением вашего кода, наиболее важным из которых является файл setup.py
.
Поскольку мы создаем пакет с нуля, сначала наш пакет будет иметь следующую структуру:
Чтобы превратить пакет в импортируемый модуль Python, каталог пакета должен содержать файл __init__.py
, который может быть просто пустым файлом, созданным с помощью touch exampy/__init__.py
. Таким образом, полноценный пакет Python выглядит как:
Без написания какого-либо дополнительного кода в разделе exampy/
(но с базовым файлом setup.py
, который мы опишем ниже) этот примерный пакет можно установить и импортировать в сеансе Python.
Файл __init__.py
содержит все, что было импортировано при помощи import exampy
или from exampy import *
(чего никогда не следует делать!). Вы можете помещать функции и классы непосредственно в файл __init__.py
или записывать их в другие файлы (для более четкой организации кода) и импортировать их в __init__.py
, чтобы сделать их легко доступными. Например, предположим, что мы реализуем первый набор основных математических функций в _math.py
, и теперь наш пакет выглядит так:
затем, не добавляя код в __init__.py
, нам нужно из from exampy import _math
получить доступ к функциям в _math.py
; например, import exampy
не разрешит доступ к exampy._math
. Если вы хотите, чтобы функции были доступны при import exampy
напрямую, вы можете импортировать их в __init__.py
следующим образом:
(хотя лучше было бы явно импортировать все функции, которые вы хотите импортировать). Это сделает функции в _math.py
, скажем, у вас есть функция def square (x): return x ** 2
, как exampy.square
, доступной через, например, from exampy import square
. В качестве альтернативы, если вы хотите сохранить часть функции «_math», вы можете сделать
что делает функцию square
доступной как exampy._math.square
. В обоих случаях мы получаем функцию square
, используя простой import exampy
. Ниже я расскажу, почему я решил начать имя файла _math.py
с подчеркивания.
Когда ваш код становится более сложным, вы, вероятно, захотите разделить функциональные возможности на разные подмодули, такие как exampy.integrate
, которые будут содержать функции для интеграции математических функций. Как мы видели выше, такая структура может быть сгенерирована с помощью одного файла integrate.py
в главном каталоге exampy/
, но чтобы позволить integrate состоять из нескольких файлов, лучше сделать каталог integrate
в разделе exampy
и использовать __init__.py
в этом каталоге, чтобы сделать его подмодулем. В этом случае макет нашего примера пакета становится
Соглашение, которому я лично следую, состоит в том, чтобы определять подмодули как можно больше через подкаталоги, а не как файлы, втягивая все функции (под)модуля в его файл __init__.py
, чтобы сделать его доступным для пользователя. Вот почему я дал файлам non-__init__.py
в приведенном выше примере имена, начинающиеся с подчеркивания. Это указывает во вселенной Python, что это внутренние части, к которым пользователи не должны обращаться напрямую; их функциональность доступна пользователям путем импорта в файл __init__.py
(под)модуля. Но это в значительной степени дело вкуса, наиболее важными соображениями являются простота для пользователя и обеспечение легкости понимания кода для себя (именно в таком порядке!).
Главное, что должен сделать файл setup.py
, - это вызвать setuptools.setup ()
, который затем позаботится о поддержке всех основных инструментов установки и упаковки. Для нашего примера пакета, приведенного выше, простой файл setup.py
выглядит следующим образом:
Поскольку установка выполняется путем запуска setup.py
как сценария Python, setup.py
может содержать произвольный код, помогающий установить ваш код. Давайте посмотрим, какие еще ключевые слова мы можем предоставить функции setup ()
. Мы можем предоставить:
url= - url с домашней страницей пакета: обычно это сайт GitHub. Дополнительные URL-адреса можно указать как project_urls=.
classifiers= которые содержат метаданные о вашем проекте, используемые PyPI для категоризации вашего пакета. Обычно используемые классификаторы касаются статуса разработки вашего кода (например, Development Status :: 4 - Beta
, Development Status
:: 6 - Mature
), целевой аудитории (например, Intended Audience :: Science/Research
), лицензии (опять же ) (например, License :: OSI Approved :: MIT License
), используемый язык программирования (например, Programming Language :: Python
или, более конкретно, Programming Language :: Python :: 3.7
), и операционная система(ы), на которой код работает на (например, Operating System :: OS Independent
). Насколько мне известно, никто никогда не использует эти классификаторы, и мне трудно не забывать обновлять их (например, между версиями Python или когда код достигает более высокого статуса разработки), но их включение считается хорошей практикой. Например, у вас может быть
Это основные описательные ключевые слова метаданных, используемые функцией setup ()
.
Дополнительные параметры функции установки помогают программам установки справиться с установкой и распространением вашего пакета:
packages= перечисляет модули и подмодули, включенные в ваш пакет. В приведенном выше примере это будут packages=["exampy","exampy/integrate"]
. Вместо того, чтобы перечислять модули вручную, вы можете использовать packages = setuptools.find_packages ()
для их автоматического поиска, убедившись, что вы включаете только свой собственный пакет, выполнив что-то вроде packages = setuptools.find_packages (include = ['exampy', 'exampy.* ']
).
python_requires= указывает версии Python, поддерживаемые вашим кодом, в основном для использования установщиком pip. Если вас это не слишком беспокоит, вы можете пропустить это, но если вы поддерживаете только Python 3 (что очень разумно в наши дни), вы можете указать python_requires = '>=3'
.
install_requires= перечисляет основные зависимости вашего кода, зависимости, без которых ваш код не может работать. Когда пользователи устанавливают ваш код с помощью pip, pip использует этот список для установки любых недостающих зависимостей. Например, чтобы указать, что ваш код требует numpy и scipy, выполните install_requires = ["numpy", "scipy"]
. Вы можете указать требования к версии, например numpy>=1.7
, используя стандартный синтаксис pip. Если у вас есть зависимость, которая не связана с PyPI (таким образом, не устанавливается pip), но есть, например, на GitHub, вы можете указать ее в install_requires и указать URL-адрес в ключевом слове dependency_links=, например, dependency_links = ["http://github.com/jobovy/galpy/tarball/master#egg=galpy"]
для ссылки на исходный код galpy на GitHub (конечно, galpy можно установить с помощью pip). В представленном выше примере exampy
мы использовали numpy в функции exampy.integrate.riemann
, поэтому нам нужно указать install_requires = ["numpy"]
.
package_data= - это словарь с любыми файлами данных, которые являются частью вашего пакета(-ов), которые необходимо скопировать в каталог установки (только файлы .py
обычно копируются в каталог установки) и которые будут распространены, когда придет время опубликовать свой пакет. Чтобы скопировать файлы данных в каталоги за пределами каталога установки, используйте data_files=. Чтобы включить README.md
и файл LICENSE
, выполните package_data={"": ["README.md", "LICENSE"]}
.
entry_points= дает нестандартные точки входа в ваш код. Например, если вы распространяете сценарий командной строки, вы можете установить его и сделать его исполняемым в пользовательской переменной PATH, указав
что делает функцию main точкой входа в my_script.
Теперь, когда у нас есть основная схема примера пакета и мы написали файл setup.py
, мы готовы к установке кода! Стандартный метод установки пакета из исходного каталога (каталог верхнего уровня, содержащий setup.py
) - это вызов
который устанавливает его в установочный каталог вашей системы (обычно в /usr/local
в системах в стиле UNIX). Вы можете указать альтернативное место установки, используя, например,
который устанавливает код в каталог в вашей домашней папке (обычно в ~/.local
в системах в стиле UNIX, с модулями, установленными в ~/.local/lib/pythonX.Y/site-packages
). Вы также можете напрямую установить префикс, используя
где выбранный здесь префикс должен иметь эквивалент опции --user
(но --prefix
может быть любым каталогом). Альтернативой прямому вызову python setup.py
является использование pip даже для локальных пакетов. Например, вы можете установить локальный проект, используя
Однако, когда вы активно разрабатываете пакет, установка описанным выше способом означает, что каждый раз, когда вы обновляете код, вы должны переустанавливать его, чтобы получить доступ ко всем внесенным вами изменениям. Чтобы избежать этого, вы можете установить пакет в режиме «develop», используя
или
если вы используете pip. В режиме «develop» исходный код не копируется в каталог установки, а делается запись в каталоге установки, чтобы найти код обратно в исходном каталоге. Это означает, что любые внесенные вами изменения сразу становятся доступными для всей системы без необходимости повторной установки. Конечно, если у вас уже есть пакет, загруженный в сеансе Python, вам все равно придется выйти и перезапустить сеанс (или использовать importlib.reload
). Если ваш пакет включает скомпилированный код, и вы вносите изменения в исходный код, который необходимо скомпилировать, вам придется перекомпилировать код, снова запустив python setup.py develop
.
Есть две основные категории лицензий с открытым исходным кодом: разрешительные лицензии на бесплатное программное обеспечение и лицензии с правом копирования.
Самым важным является то, что вы предоставляете своему коду лицензию, причем тип лицензии имеет второстепенное значение; любая лицензия лучше, чем отсутствие лицензии. Хотя вам это может показаться глупым, явный отказ от ответственности - важная вещь, которую нужно сделать, когда вы размещаете код в сети, чтобы юридически защитить себя от неправильного использования вашего кода (не то, чтобы это когда-либо случалось со мной, но вы никогда не знаете ...) . В случае сомнений выберите разрешающую лицензию, например, MIT License.
Все, что мы обсуждали до сих пор для основного содержимого каталога exampy/
, справедливо и для этого подмодуля: мы можем писать код в каталоге integration/__init__.py
или в разных файлах в этом каталоге. Например, представьте, что у нас есть файл integration/_integrate.py
, который реализует простую def riemann (func, a, b, n = 10): return np.sum (func (np.linspace (a, b, n )) * (b-a) / n
). Затем с пустым файлом integration/__init__.py
мы должны импортировать import exampy.integrate._integrate
, чтобы получить доступ к exampy.integrate._integrate.riemann
(или from exampy.integrate import _integrate
или аналогичного), или мы можем снова импортировать функцию riemann
в integrate/__init__.py
, чтобы сделать его доступным с помощью простого вызова from exampy import integrate
.
Соображения при именовании подмодулей аналогичны тем, которые обсуждались при именовании пакета : выбирайте короткие описательные имена (в данном случае не умные; отличными примерами являются scipy.integrate
, scipy.interpolate
, которые сразу дают понять, что эти подмодули делать и не делать).
Затем мы хотим сделать наш пакет доступным для установки с помощью стандартных инструментов установки Python. Основной инструмент, используемый для упаковки Python, - это . Чтобы использовать setuptools, мы пишем файл setup.py
, который включает всю информацию, необходимую для сборки, установки и упаковки кода.
Некоторые пакеты используют файл конфигурации setup.cfg
для определения необходимой информации, но даже в этом случае все равно необходимо написать файл setup.py
, который принимает файл конфигурации и подключает его к setuptools. Хотя у этого есть некоторые преимущества, я думаю, что для начинающих пользователей проще напрямую написать файл setup.py
, который является поучительным и также допускает обширную настройку позже. Еще один недостаток использования файла setup.cfg
заключается в том, что из-за него python se [TAB]
больше не завершается автоматически до python setup.py
! Расширенные файлы setup.py
могут быть довольно сложными (например, взгляните на ), поэтому, хотя снова поучительно посмотреть файлы setup.py
других пакетов, но новичков это, вероятно, сильно запутает.
Этот базовый файл setup.py
определяет имя пакета, его версию, некоторую базовую информацию об авторе и пакете, а также сообщает setuptools, что такое фактический пакет. Если вы добавите этот файл в пример пакета, вы сможете установить его, выполнив установку python setup.py
, но подробнее о том, как установить код, см. .
long_description: это подробное описание того, что делает код (длиннее, чем description, которое должно быть одним предложением) и что в конечном итоге будет опубликовано на сайте PyPI пакета (например, см. страницу ). Обычно можно использовать тот факт, что мы можем запустить произвольный код в файле setup.py
, чтобы прочитать содержимое README и использовать его как long_description, используя:
в случае, если формат README - это , и мы также указываем формат.
license= с названием лицензии с (например, license = 'New BSD'
или license = 'MIT'
).
для зрелого пакета, используемого в астрофизике, который работает с последними версиями Python во всех операционных системах. Полный список классификаторов доступен .
Более подробную информацию о ключевых словах setup ()
можно найти на .
Прежде чем перейти к следующей главе, где я расскажу, как начать делиться своим кодом в сети с другими, важно кратко обсудить лицензии на код. У всего кода, размещенного в Интернете, должна быть лицензия. Без лицензии, определяющей условия использования и повторного распространения кода, весь код считается защищенным авторским правом автору, без разрешения повторного использования или повторного распространения (код, который вы размещаете в Интернете без лицензии, не является общественным достоянием, действительно, дело обстоит наоборот). Таким образом, вы должны выбрать лицензию для своего кода и поместить файл лицензии в каталог верхнего уровня вашего кода. Если код находится на GitHub без лицензии, Условия использования GitHub позволяют людям просматривать и разветвлять код, но никакие модификации или повторное распространение не разрешены (см. страницу справки GitHub ). Лицензируйте свой код.
Разрешительные лицензии, как следует из их названия, очень щедры по своим условиям. Как правило, они допускают произвольное использование, модификацию и повторное распространение при условии сохранения исходной лицензии, должного упоминания оригинального автора и явного отказа от любой ответственности, связанной с любым использованием кода. Примерами разрешительных лицензий являются и , при этом Лицензия MIT является разрешающей лицензией по выбору для недавних проектов. Разрешительные лицензии позволяют максимально широко использовать ваш код, потому что они требуют очень небольшого количества людей, использующих ваш код. Большинство основных проектов Python, которые вы знаете и любите, используют разрешительное лицензирование (например, numpy
, scipy
, astropy
).
Лицензии с левым авторским правом - это лицензии с открытым исходным кодом, которые, помимо отказа от ответственности и запроса кредита на первоначального автора, также требуют, чтобы любые модификации кода были повторно распространены под аналогичной лицензией с левым авторским правом. Основным используемым примером лицензии с правом копирования является (есть также более старая , которая несколько более допустима). Таким образом, вы можете использовать код с лицензией с левым копированием только в пакетах, которые сами лицензированы с левым копированием. На практике это снижает принятие таких пакетов, даже несмотря на то, что философия, лежащая в основе этого стиля лицензии, заслуживает похвалы (она направлена на то, чтобы программное обеспечение с открытым исходным кодом оставалось открытым).
обычно не используются для программного обеспечения, хотя они активно используются для совместного использования другого творческого контента, такого как веб-сайты, материалы классов, научные статьи, сообщения в блогах и т. д.