Модули

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

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

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

# Fibonacci numbers module

def fib(n):    # write Fibonacci series up to n
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a+b
    print()

def fib2(n):   # return Fibonacci series up to n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result

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

>>> import fibo

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Примечание

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

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

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

python fibo.py <arguments>

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

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

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

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

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

>>> import fibo
>>>

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

Путь поиска модулей

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

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

Примечание

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

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

«Скомпилированные» файлы 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.

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

Python поставляется с библиотекой стандартных модулей, описанной в отдельном документе, Справочнике по библиотеке Python (далее — «Справочнику по библиотеке»). Некоторые модули встроены в интерпретатор. Они обеспечивают доступ к операциям, не входящим в ядро языка, и встроены для большей эффективности и предоставления доступа к основным средствам операционной системы, таким как системные вызовы (system calls). Набор таких модулей — выбор настройки, зависимый от используемой платформы. Например, модуль 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')

Функция dir()

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

>>> import fibo, sys
>>> dir(fibo)
['__name__', 'fib', 'fib2']
>>> dir(sys)  # doctest: +NORMALIZE_WHITESPACE
['__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)  # doctest: +NORMALIZE_WHITESPACE
['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']

Пакеты

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

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

sound/                          Top-level package
      __init__.py               Initialize the sound package
      formats/                  Subpackage for file format conversions
              __init__.py
              wavread.py
              wavwrite.py
              aiffread.py
              aiffwrite.py
              auread.py
              auwrite.py
              ...
      effects/                  Subpackage for sound effects
              __init__.py
              echo.py
              surround.py
              reverse.py
              ...
      filters/                  Subpackage for 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 элемент.подэлемент.подэлемент, все элементы кроме последнего должны быть пакетами; последний элемент может быть модулем или пакетом, но не может быть классом, функцией или переменной, определёнными в предыдущем элементе.

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

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

Единственное решение для автора пакета — обеспечить явный список имен пакета. Оператор import использует следующее соглашение: если код __init__.py пакета определяет список с именем __all__, то он выбирается в качестве списка имен модуля для импорта, когда встречается from package import *. На усмотрение автора пакета — поддержка этого списка в обновленном состоянии, когда выпускается новая весия пакета. Авторы пакетов могут также решить не поддерживать его, если не актуален импорт * из их пакета. Например, файл sounds/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 *, все равно это считается плохой практикой в production-коде.

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

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

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

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

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

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

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

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

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

Сноски

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