Введение
Если вы пишете более крупную программу, вы не хотите организовывать ее как большой набор отдельных файлов на верхнем уровне. В этом разделе представляется концепция пакета.
This tutorial is from open-source community. Access the source code
💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал
Если вы пишете более крупную программу, вы не хотите организовывать ее как большой набор отдельных файлов на верхнем уровне. В этом разделе представляется концепция пакета.
Любой исходный файл на Python - это модуль.
## foo.py
def grok(a):
...
def spam(b):
...
Инструкция import
загружает и исполняет модуль.
## program.py
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
Для более крупных коллекций кода распространено организовывать модули в пакет.
## От этого
pcost.py
report.py
fileparse.py
## До этого
porty/
__init__.py
pcost.py
report.py
fileparse.py
Вы выбираете имя и создаете верхнеуровневую директорию. porty
в примере выше (ясно, что выбор этого имени - это самый важный первый шаг).
Добавьте файл __init__.py
в директорию. Он может быть пустым.
Разместите исходные файлы в директории.
Пакет служит именнованным пространством для импортов.
Это означает, что теперь существуют импорты多层次.
import porty.report
port = porty.report.read_portfolio('portfolio.csv')
Существуют и другие варианты инструкций import.
from porty import report
port = report.read_portfolio('portfolio.csv')
from porty.report import read_portfolio
port = read_portfolio('portfolio.csv')
При этом подходе есть две главные проблемы.
Таким образом, в основном все сломается. Но, помимо этого, это работает.
Импорты между файлами в одном пакете теперь должны включать имя пакета в импорте. Помните структуру.
porty/
__init__.py
pcost.py
report.py
fileparse.py
Измененный пример импорта.
from porty import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
Все импорты абсолютные, а не относительные.
import fileparse ## BREAKS. fileparse не найден
...
Вместо прямого использования имени пакета можно использовать .
для ссылки на текущий пакет.
from. import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
Синтаксис:
from. import modname
Это упрощает переименование пакета.
Запуск подмодуля пакета в качестве основного скрипта завершается ошибкой.
$ python porty/pcost.py ## BREAKS
...
Причина: Вы запускаете Python на одном файле, и Python не видит остальную структуру пакета правильно (sys.path
неправильный).
Все импорты завершаются ошибкой. Чтобы исправить это, вам нужно запускать программу по-другому, используя параметр -m
.
$ python -m porty.pcost ## WORKS
...
__init__.py
Основная цель этих файлов - скрепить модули вместе.
Пример: объединение функций
## porty/__init__.py
from.pcost import portfolio_cost
from.report import portfolio_report
Это позволяет именам появляться на верхнем уровне при импорте.
from porty import portfolio_cost
portfolio_cost('portfolio.csv')
Вместо использования многоуровневых импортов.
from porty import pcost
pcost.portfolio_cost('portfolio.csv')
Как уже упоминалось, теперь для запуска скриптов внутри пакета нужно использовать -m package.module
.
$ python3 -m porty.pcost portfolio.csv
Есть другой вариант: написать новый верхнеуровневый скрипт.
#!/usr/bin/env python3
## pcost.py
import porty.pcost
import sys
porty.pcost.main(sys.argv)
Этот скрипт находится за пределами пакета. Например, рассмотрим структуру каталогов:
pcost.py ## верхнеуровневый скрипт
porty/ ## каталог пакета
__init__.py
pcost.py
...
Организация кода и структура файлов являются ключевыми для поддержки работоспособности приложения.
Для Python нет подхода, который бы "подошел для всех случаев". Однако одна структура, которая подходит для многих задач, выглядит примерно так.
porty-app/
README.txt
script.py ## СКРИПТ
porty/
## КОД БИБЛИОТЕКИ
__init__.py
pcost.py
report.py
fileparse.py
Верхний уровень porty-app
является контейнером для всего остального - документации, верхнеуровневых скриптов, примеров и т.д.
Опять же, верхнеуровневые скрипты (если есть) должны находиться вне пакета кода. На одном уровне выше.
#!/usr/bin/env python3
## porty-app/script.py
import sys
import porty
porty.report.main(sys.argv)
На этом этапе у вас есть каталог с несколькими программами:
pcost.py ## вычисляет стоимость портфеля
report.py ## Создает отчет
ticker.py ## Генерирует реальный-time биржевую ленту
Есть также различные вспомогательные модули с другой функциональностью:
stock.py ## Класс акции
portfolio.py ## Класс портфеля
fileparse.py ## Парсинг CSV
tableformat.py ## Форматированные таблицы
follow.py ## Следить за лог-файлом
typedproperty.py ## Типизированные свойства класса
В этом упражнении мы собираемся очистить код и поместить его в общий пакет.
Создайте директорию с именем porty/
и поместите все вышеперечисленные Python-файлы в нее. Кроме того, создайте пустой файл __init__.py
и поместите его в директорию. У вас должна быть директория с файлами следующего вида:
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
Удалите файл __pycache__
, находящийся в вашей директории. В нем содержатся предварительно скомпилированные Python-модули из предыдущего времени. Мы хотим начать с чистого листа.
Попробуйте импортировать некоторые модули пакета:
>>> import porty.report
>>> import porty.pcost
>>> import porty.ticker
Если эти импорты не удаются, перейдите в соответствующий файл и исправьте импорты модулей, чтобы включить относительный импорт пакета. Например, инструкция вида import fileparse
может измениться следующим образом:
## report.py
from. import fileparse
...
Если у вас есть инструкция вида from fileparse import parse_csv
, измените код на следующий:
## report.py
from.fileparse import parse_csv
...
Поместить весь ваш код в "пакет" часто недостаточно для приложения. Иногда есть вспомогательные файлы, документация, скрипты и прочие вещи. Эти файлы должны находиться ВНЕ директории porty/
, которую вы создали выше.
Создайте новую директорию с именем porty-app
. Переместите директорию porty
, созданную вами в Упражнении 9.1, в эту директорию. Скопируйте тестовые файлы portfolio.csv
и prices.csv
в эту директорию. Кроме того, создайте файл README.txt
с некоторой информацией о себе. Ваш код теперь должен быть организован следующим образом:
porty-app/
portfolio.csv
prices.csv
README.txt
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
Для запуска вашего кода вы должны убедиться, что вы работаете в верхнем уровне директории porty-app/
. Например, из терминала:
$ cd porty-app
$ python3
>>> import porty.report
>>>
Попробуйте запустить некоторые из ваших предыдущих скриптов в качестве основного программы:
$ cd porty-app
$ python3 -m porty.report portfolio.csv prices.csv txt
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
$
Использование команды python -m
часто выглядит несколько странно. Возможно, вы захотите написать верхнеуровневый скрипт, который просто будет обрабатывать специфические аспекты пакетов. Создайте скрипт print-report.py
, который генерирует вышеупомянутый отчет:
#!/usr/bin/env python3
## print-report.py
import sys
from porty.report import main
main(sys.argv)
Разместите этот скрипт в верхнем уровне директории porty-app/
. Убедитесь, что вы можете запустить его в этом месте:
$ cd porty-app
$ python3 print-report.py portfolio.csv prices.csv txt
Name Shares Price Change
---------- ---------- ---------- ----------
AA 100 9.22 -22.98
IBM 50 106.28 15.18
CAT 150 35.46 -47.98
MSFT 200 20.89 -30.34
GE 95 13.48 -26.89
MSFT 50 20.89 -44.21
IBM 100 106.28 35.84
$
Ваш окончательный код теперь должен быть структурирован примерно так:
porty-app/
portfolio.csv
prices.csv
print-report.py
README.txt
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
Поздравляем! Вы завершили лабораторную работу по пакетам. Вы можете практиковаться в других лабораторных работах в LabEx, чтобы улучшить свои навыки.