6. Модули

Если вы выйдете из интерпретатора и зайдёте в него снова, то все определённые вами имена (функции и переменные) будут потеряны. По этой причине, если вы захотите написать несколько более длинную программу, вам лучше использовать текстовый редактор для подготовки ввода для интерпретатора и запускать последний в режиме файлового ввода. Это называется созданием сценария. Если ваша программа становится обширнее, вы можете предпочесть разделить её на несколько файлов для удобства эксплуатации. Также вы можете захотеть использовать сразу в нескольких программах некоторую полезную функцию, написанную вами, не копируя её определение каждый раз.

В языке Python можно поместить требуемые определения в файл и использовать их в сценариях или в интерактивном режиме интерпретатора. Такой файл называется модулем; определения из модуля можно импортировать в другие модули или в главный модуль (набор переменных, к которым у вас есть доступ в скрипте, выполняемом на верхнем уровне и в режиме калькулятора).

Модуль — это файл, содержащий определения и Python инструкции. Именем файла является имя модуля с добавленным суффиксом .py. Внутри модуля, имя модуля (в качестве строки) доступно в виде значения глобальной переменной с именем __name__. Например, используя ваш любимый текстовый редактор, создайте в текущем каталоге файл с именем fibo.py со следующим содержимым:

# Модуль чисел Фибоначчи

def fib(n):    # распечатать ряд Фибоначчи до n
    a, b = 0, 1
    while a < n:
        print(a, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # вернуть ряд Фибоначчи до n
    result = []
    a, b = 0, 1
    while a < n:
        result.append(a)
        a, b = b, a+b
    return result

Теперь можно войти в интерпретатор Python и импортировать этот модуль следующей командой:

>>> import fibo

Имена функций, определенных в fibo, не вводятся непосредственно в текущую таблицу символов; здесь вводится только имя модуля fibo. С помощью имени модуля можно получить доступ к функциям:

>>> fibo.fib(1000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987
>>> fibo.fib2(100)
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>> fibo.__name__
'fibo'

Если вы собираетесь использовать функцию часто, можно присвоить её локальному имени:

>>> fib = fibo.fib
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

6.1. Подробнее о модулях

Помимо определений функций модуль может содержать исполняемые операторы. Назначение этих операторов — инициализация модуля. Они выполняются при первом импортировании модуля где-либо [1] (они также прогоняются, если файл выполняется как скрипт).

Каждый модуль имеет свою собственную таблицу символов, которая используется в качестве глобальной всеми определёнными в модуле функциями. Таким образом, автор модуля может использовать глобальные символы в модуле, не опасаясь неожиданных совпадений с глобальными переменными пользователя. С другой стороны, если вы знаете, что делаете, можно сослаться на глобальные переменные модуля, пользуясь той же нотацией, которая применялась для ссылок на его функции: modname.itemname.

Модули могут импортировать другие модули. Не требуется указывать все инструкции import в начале модуля (или сценария, с той же целью), но обычно так и делается. Имена из импортированного модуля добавляются в глобальную таблицу символов модуля его импортирующего.

Существует вариант инструкции import, который переносит имена из модуля прямо в таблицу символов импортирующего модуля. Например:

>>> from fibo import fib, fib2
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

При этом имя самого модуля, из которого переносятся имена элементов, не добавляется в локальную таблицу символов (так в примере fibo не определена).

И есть даже способ импортировать все имена, которые определяет данный модуль:

>>> from fibo import *
>>> fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Импортируются все имена, кроме тех, которые начинаются на подчёркивание (_). В большинстве случаев программисты на Python не используют эту возможность, поскольку она внедряет в интерпретатор целый набор новых неизвестных имён и может скрыть некоторые объекты, которые вы уже определили.

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

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

>>> import fibo as fib
>>> fib.fib(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Это позволяет эффективно импортировать модуль таким же образом, как и import fibo, при этом единственное отличие состоит в том, что модуль доступен как fib.

Его также можно использовать при использовании from с аналогичными эффектами:

>>> from fibo import fib as fibonacci
>>> fibonacci(500)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377

Примечание

По соображениям эффективности каждый модуль импортируется только один раз за сеанс работы с интерпретатором. Поэтому при изменении модулей необходимо перезапустить интерпретатор или, если это только один модуль, который требуется тестировать в интерактивном режиме, использовать функцию: importlib.reload, например import importlib; importlib.reload (modulename).

6.1.1. Выполнение модулей как скриптов

Когда вы запускаете модуль Python в виде:

python fibo.py <arguments>

то код в этом модуле будет исполнен в момент его импортирования, но значением __name__ будет строка "__main__". Это значит, что добавляя следующий код в конец сценария:

if __name__ == "__main__":
    import sys
    fib(int(sys.argv[1]))

Вы можете сделать возможным запуск файла и в качестве сценария, и в качестве импортируемого модуля. Это возможно, поскольку разбирающий командную строку код выполняется только при исполнении модуля как «главного» файла:

$ python fibo.py 50
0 1 1 2 3 5 8 13 21 34

Если модуль импортируется, код не будет выполнен:

>>> import fibo
>>>

Такой приём часто используется, чтобы предоставить удобный пользовательский интерфейс к модулю или для тестирования (выполнение модуля в качестве сценария запускает набор тестов).

6.1.2. Путь поиска модуля

При импорте модуля с именем spam интерпретатор сначала выполняет поиск встроенный модуль с таким именем. Если файл не найден, выполняется поиск файла имя spam.py в списке каталогов, заданных переменной sys.path. sys.path инициализированы из следующих мест:

  • Каталог, содержащий входной сценарий (или текущий каталог, если файл не указан).
  • PYTHONPATH (список имен каталогов с тем же синтаксисом, что и переменная оболочки PATH).
  • Зависит от установки по умолчанию.

Примечание

В файловых системах, которые поддерживают символические ссылки, директория, содержащая входной сценарий, рассчитывается следования по симлинку. Другими словами, директория, содержащая симлинк, не добавляется в путь поиска модулей.

После инициализации Python программы могут изменять sys.path. Директория, содержащая выполняемый скрипт, добавляется в начало путей поиска, раньше пути стандартной библиотеки. Это означает, что скрипты в этой директории будут загружаться вместо модулей с тем же именем из директории библиотеки. Это является ошибкой, если только замена не задумана намеренно. Для более подробной информации обратитесь к разделу Стандартные модули.

6.1.3. «Скомпилированные» файлы Python

Для ускорения загрузки модулей Python кэширует скомпилированную версию каждого модуля в каталоге __pycache__ под именем module.version.pyc, где version кодирует формат скомпилированного файла; как правило, она содержит номер версии Python. Например, в CPython выпуске 3.3 скомпилированные версия spam.py кэшируется как __pycache__/spam.cpython-33.pyc. Такая конвенция именования позволяет сосуществовать скомпилированным модулям различных выпусков и версий Python.

Python проверяет дату изменения исходника относительно скомпилированной версии, чтобы понять, устарела ли она и требует ли перекомпиляции. Это полностью автоматический процесс. Кроме того, скомпилированные модули являются платформо-независимыми, так что та же самая библиотека может быть совместно использована между системами с различной архитектурой.

Python не проверяет кэш в двух ситуациях. Во-первых, он всегда перекомпилирует и не хранит результат для модулей, который загружают прямо из командной строки. Во-вторых, он не проверяет кэш, если нет исходника модуля. Для поддержки распространения без исходников (только скомпилированного), скомпилированный модуль должен быть в директории исходника, и исходного модуля быть не должно.

Некоторые советы для экспертов:

  • Для уменьшения размера скомпилированного модуля можно использовать -O или -OO опции в Python командах. Ключ -O удаляет инструкции assert, ключ -OO удаляет инструкции assert и строки __doc__. Так как некоторые программы могут рассчитывать на их наличие, вам следует использовать эту опцию, если вы знаете, что делаете. «Оптимизированные» модули имеют отметку opt- и, как правило, меньше. Будущие релизы могут изменить эффекты оптимизации.
  • Программа не выполняется быстрее, когда она читается из файла .pyc, а не из .py; единственное, в чем быстрее .pyc-файлы — скорость, с которой они загружаются.
  • Модуль compileall может создавать файлы .pyc для всех модулей в каталоге.
  • Более подробно об этом процессе, включая логическую блок-схему, см. в PEP 3147.

6.2. Стандартные модули

Python поставляется с библиотекой стандартных модулей, описанной в отдельном документе, Справочнике по библиотеке Python (далее — «Справочнику по библиотеке»). Некоторые модули встроены в интерпретатор. Они обеспечивают доступ к операциям, не входящим в ядро языка, и встроены для большей эффективности и предоставления доступа к основным средствам операционной системы, таким как системные вызовы. Набор таких модулей — выбор настройки, зависимый от используемой платформы. Например, модуль winreg поставляется только в Windows системах. Один конкретный модуль заслуживает определенного внимания: sys, который встроен в каждый Python интерпретатор. Переменные sys.ps1 и sys.ps2 определяют строки, использующиеся в качестве основного и вспомогательного приглашений:

>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Yuck!')
Yuck!
C>

Эти две переменные определены только для интерактивного режима интерпретатора.

Переменная sys.path представляет собой список строк, определяющий путь поиска модулей. Он инициализируется значением путей по умолчанию, взятым из PYTHONPATH переменной среды или из встроенного значения по умолчанию, если PYTHONPATH не установлен. Изменить его можно с помощью стандартного списка операции:

>>> import sys
>>> sys.path.append('/ufs/guido/lib/python')

6.3. Функция dir()

Встроенная функция dir() используется для получения имён, определённых в модуле. Она возвращает отсортированный список строк:

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)  
['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__',
 '__package__', '__stderr__', '__stdin__', '__stdout__',
 '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe',
 '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv',
 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder',
 'call_tracing', 'callstats', 'copyright', 'displayhook',
 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix',
 'executable', 'exit', 'flags', 'float_info', 'float_repr_style',
 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit',
 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount',
 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
 'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path',
 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1',
 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit',
 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout',
 'thread_info', 'version', 'version_info', 'warnoptions']

Без аргументов в dir() перечислены имена, определенные в данный момент:

>>> a = [1, 2, 3, 4, 5]
>>> import fibo
>>> fib = fibo.fib
>>> dir()
['__builtins__', '__name__', 'a', 'fib', 'fibo', 'sys']

Обратите внимание, что в нем перечислены все типы имен: переменные, модули, функции и т.д.

dir() не перечисляет имена встроенных функций и переменных. Если вам требуется их список, они определены в стандартном модуле builtins:

>>> import builtins
>>> dir(builtins)  
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException',
 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning',
 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError',
 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning',
 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False',
 'FileExistsError', 'FileNotFoundError', 'FloatingPointError',
 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError',
 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError',
 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError',
 'MemoryError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented',
 'NotImplementedError', 'OSError', 'OverflowError',
 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError',
 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning',
 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError',
 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError',
 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError',
 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning',
 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__build_class__',
 '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs',
 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable',
 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits',
 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit',
 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr',
 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass',
 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview',
 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property',
 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice',
 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars',
 'zip']

6.4. Пакеты

Пакеты — способ структурирования пространств имён модулей Python за счёт использования имён модулей, разделённых точками. Например, имя A.B обозначает подмодуль с именем B в пакете с именем A. Также как использование модулей позволяет авторам различных модулей не заботиться о пересекающихся именах среди глобальных переменных, использование именования через точку позволяет авторам многомодульных пакетов (таких как NumPy или PIL) не заботиться о конфликтах имён модулей.

Допустим, вы собираетесь разработать набор модулей («пакет») для унифицированной обработки звуковых файлов и звуковых данных. Существует много различных звуковых форматов файлов (обычно распознаются по их расширению, например .wav, .aiff, .au). Таким образом, вам может понадобиться создать и поддерживать разрастающуюся коллекцию модулей для конвертирования между различными форматами файлов. Также вам наверняка захочется иметь побольше операций для обработки звуковых данных (таких как смешивание, добавление эха, применение функции эквалайзера, создание искусственного стерео-эффекта), поэтому в дополнение к этому вы будете писать нескончаемый поток модулей для исполнения этих операций. Вот возможная структура вашего пакета (выраженная в терминологии иерархической файловой системы):

sound/                          Пакет верхнего уровня
      __init__.py               Инициализировать звуковой пакет
      formats/                  Подпакет для преобразования форматов файлов
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Подпакет звуковых эффектов
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Подпакет для фильтров
              __init__.py
              equalizer.py
              vocoder.py
              karaoke.py
              ...

При импорте пакета Python ищет подкаталог пакета в каталогах, перечисленных в sys.path.

Файлы __init__.py необходимы для того, чтобы Python трактовал эти каталоги как содержащие пакеты. Это сделано во избежание нечаянного сокрытия правомерных модулей, встречающихся в дальнейшем по пути поиска, каталогами с часто используемыми именами, таким как string. В наипростейшем случае файл __init__.py может быть пустым, но в более сложных может содержать код инициализации пакета или устанавливать значение описанной ниже переменной __all__.

Пользователи пакета могут импортировать отдельные модули из пакета, например:

import sound.effects.echo

Загружает подмодуль sound.effects.echo. Ссылаться на него нужно используя его полное имя:

sound.effects.echo.echofilter(input, output, delay=0.7, atten=4)

Альтернативный способ импорта подмодуля:

from sound.effects import echo

Также загружает echo подмодуля и делает его доступным без префикса пакета, поэтому его можно использовать следующим образом:

echo.echofilter(input, output, delay=0.7, atten=4)

Еще одним вариантом является прямой импорт требуемой функции или переменной:

from sound.effects.echo import echofilter

Опять же, этот код загружает echo подмодуль, но это делает его функцию непосредственно доступный echofilter():

echofilter(input, output, delay=0.7, atten=4)

Заметьте, что при использовании выражения from package import item, элементом может быть подмодуль (или подпакет) пакета или любое другое имя, определённое в пакете — например, функция, класс или переменная. Оператор import сначала проверяет, определён ли элемент в пакете; если нет — он трактует его как модуль и пытается загрузить. Если не удается его найти, порождается исключение ImportError.

Напротив, при использовании синтаксиса в стиле import item.subitem.subsubitem, все элементы кроме последнего должны быть пакетами; последний элемент может быть модулем или пакетом, но не может быть классом, функцией или переменной, определёнными в предыдущем элементе.

6.4.1. Импорт * из пакета

Что происходит, когда пользователь пишет from sound.effects import *? В идеале, мы бы надеялись, что таким образом код выходит в файловую систему и находит какие подмодули существуют в пакете, импортируя их все. Это может занять много времени и импорт под-модулей может иметь нежелательныее побочные эффекты, которые должны происходить только когда этот подмодуль явно импортируется.

Единственным решением является предоставление автором пакета явного списка имен пакета. Инструкция import использует следующее соглашение: если код __init__.py пакета определяет список с именем __all__, то он выбирается в качестве списка имен модуля для импорта, когда встречается from package import *. На усмотрение автора пакета — поддержка этого списка в обновленном состоянии, когда выпускается новая версия пакета. Авторы пакетов могут также решить не поддерживать его, если не актуален импорт * из их пакета. Например, файл sound/effects/__init__.py может содержать следующий код:

__all__ = ["echo", "surround", "reverse"]

Это будет значить, что выражение from sound.effects import * импортирует три именованных подмодуля из пакета sound.

Если __all__ не определен, инструкция from sound.effects import * не импортирует все подмодули из sound.effects пакета в текущее пространство имен; он обеспечивает только импорт sound.effects пакета (возможно, запуск любой код инициализации в __init__.py), а затем импорт любых имен, определенных в пакете. Сюда входят любые имена, определенные __init__.py (и явно загруженные подмодули). Он также включает любые подмодули пакета, которые были явно загружены предыдущими import инструкции. Рассмотрим код:

import sound.effects.echo
import sound.effects.surround
from sound.effects import *

В этом примере модули echo и surround импортируются в текущее пространство имен, поскольку они определяются в пакете sound.effects при выполнении инструкции from...import. (Это также работает при определении __all__.)

Несмотря на то, что некоторые модули спроектированы так, что экспортируют имена согласно некоторой логике при использовании import *, все равно это считается плохой практикой в производственном-коде.

Помните, нет ничего плохого в использовании from package import specific_submodule! На самом деле — это рекомендованная запись, до тех пор пока при импортировании модуля не нужно использовать подмодули с одинаковым именем из разных пакетов.

6.4.2. Ссылки внутри пакета

При структурировании пакетов в подпакеты (пример, как в случае с пакетом sound), можно использовать абсолютный импорт для ссылки на подмодули пакетов братьев и сестер. Например, если необходимо использовать sound.filters.vocoder модуль echo в пакете sound.effects, он должен использовать from sound.effects import echo.

Вы можете также использовать относительное импортирование, применяя следующую форму инструкции import: from module import name. При таком способе импортирования для описания текущего и родительского пакетов используется символ точки. Например, для модуля surround вы можете написать:

from . import echo
from .. import formats
from ..filters import equalizer

Обратите внимание, что относительное импортирование основано на имени текущего модуля. Поскольку имя главного модуля всегда "__main__", модули, предназначенные для использования в качестве главных модулей приложения на Python, должны всегда использовать абсолютное импортирование.

6.4.3. Пакеты в нескольких каталогах

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

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

Сноски

[1]На самом деле определения также являются «операторами», которые исполняют; выполнение определения функций на уровне модуля вводит имя функции в глобальную таблицу символов модуля.