содержание| Предыдущий раздел (8.3 Отладка) | Следующий раздел (9.2 Сторонние пакеты)
9.1 Пакет
Если мы пишем большую программу, мы не хотим организовывать ее на верхнем уровне в виде большой коллекции отдельных файлов. В этом разделе представлены пакеты.
модуль
Любой исходный файл Python называется модулем.
# foo.py
def grok(a):
...
def spam(b):
...
одинimport
оператор загружает ивоплощать в жизньмодуль.
# program.py
import foo
a = foo.grok(2)
b = foo.spam('Hello')
...
пакет против модуля
Для больших коллекций кода модули обычно объединяются в пакеты.
# From this
pcost.py
report.py
fileparse.py
# To this
porty/
__init__.py
pcost.py
report.py
fileparse.py
Сначала выберите имя и создайте каталог верхнего уровня с этим именем. как указано вышеporty
(Очевидно, что самым важным первым шагом является выбор имени).
Затем добавьте__init__.py
файлы в этот каталог.__init__.py
файл может быть пустым файлом.
Наконец, поместите исходные файлы в этот каталог.
использовать пакет
Пакеты используются как пространства имен для импорта.
Это означает, что теперь есть многоуровневый импорт.
import porty.report
port = porty.report.read_portfolio('port.csv')
Существуют и другие варианты операторов импорта:
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
Пример измененного импорта в соответствии с указанными выше правилами (импорт между разными файлами в одном пакете должен содержать имя пакета):
# report.py
from porty import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
Все импорты являются абсолютными, а не относительными.
# report.py
import fileparse # BREAKS. fileparse not found
...
относительный импорт
Помимо прямого импорта с использованием имени пакета, вы также можете использовать.
Ссылка на текущий пакет.
# report.py
from . import fileparse
def read_portfolio(filename):
return fileparse.parse_csv(...)
грамматика:
from . import modname
Использование приведенного выше синтаксиса упрощает переименование пакетов.
Проблема: основной скрипт
Запуск подмодуля внутри пакета в качестве основного скрипта приводит к поломке программы:
bash $ python porty/pcost.py # BREAKS
...
Причина: вы запускаете один скрипт, а Python не знает об остальном пакете (sys.path
это не правильно).
Весь импорт сломается. Чтобы решить эту проблему, вам нужно запустить программу другим способом, вы можете использовать-m
опции.
bash $ 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
Запустите скрипт внутри пакета.
bash % python3 -m porty.pcost portfolio.csv
Есть еще вариант: написать новый скрипт верхнего уровня.
#!/usr/bin/env python3
# pcost.py
import porty.pcost
import sys
porty.pcost.main(sys.argv)
Скрипт находится вне пакета. Структура каталогов следующая:
pcost.py # top-level-script
porty/ # package directory
__init__.py
pcost.py
...
Структура приложения
Организация кода и файловая структура являются ключом к ремонтопригодности приложения.
Не существует универсального подхода к Python, но есть одна структура, которая работает для многих видов проблем:
porty-app/
README.txt
script.py # SCRIPT
porty/
# LIBRARY CODE
__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 # computes portfolio cost
report.py # Makes a report
ticker.py # Produce a real-time stock ticker
При этом имеется множество вспомогательных модулей с различными функциями:
stock.py # Stock class
portfolio.py # Portfolio class
fileparse.py # CSV parsing
tableformat.py # Formatted tables
follow.py # Follow a log file
typedproperty.py # Typed class properties
В этом упражнении мы организуем эти коды и поместим их в общий пакет.
Упражнение 9.1. Создание простого пакета
Пожалуйста, создайтеporty
каталог и поместите в него все вышеперечисленные файлы Python. Кроме того, вporty
создать пустой каталог__init__.py
документ. Наконец, каталог файлов выглядит так:
porty/
__init__.py
fileparse.py
follow.py
pcost.py
portfolio.py
report.py
stock.py
tableformat.py
ticker.py
typedproperty.py
пожалуйстаporty
в каталоге__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
...
Упражнение 9.2. Создание каталога приложений
Обычно приложению недостаточно поместить весь код в «пакет». Иногда необходимо поместить вспомогательные файлы, документацию, сценарии и т. д. вporty/
вне каталога.
Пожалуйста, создайтеporty-app
новый каталог. Затем используйте тот, который мы создали в упражнении 9.1.porty
каталог перемещен вporty-app
в каталоге. Далее копируем тестовый файлData/portfolio.csv
иData/prices.csv
прибытьporty-app
содержание. Кроме того, вporty-app
создать каталог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/
Вниз. Например, из терминала запустите:
shell % cd porty-app
shell % python3
>>> import porty.report
>>>
Попробуйте запустить предыдущий скрипт в качестве основной программы:
shell % cd porty-app
shell % 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
shell %
Упражнение 9.3. Сценарий верхнего уровня
использоватьpython -m
Команды обычно немного странные. Может потребоваться написать сценарий верхнего уровня для обработки странных пакетов. Пожалуйста, создайте скрипт, который генерирует вышеупомянутый отчетprint-report.py
:
#!/usr/bin/env python3
# print-report.py
import sys
from porty.report import main
main(sys.argv)
потом поставь скриптprint-report.py
положить в верхний каталогporty-app/
середина. и убедитесь, чтоporty-app/
Запустите его в каталоге:
shell % cd porty-app
shell % 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
shell %
В итоге организация кода должна выглядеть так:
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
содержание| Предыдущий раздел (8.3 Отладка) | Следующий раздел (9.2 Сторонние пакеты)
Примечание. Полный перевод см.GitHub.com/co Статья 3 — /PRA…