6. Модули

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

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

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

# Fibonacci numbers module

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

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

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

>>> import fibo

Это не добавляет имена функций, определенных в fibo, непосредственно в текущий namespace (см. Области и пространства имен Python для более подробной информации), а только добавляет туда имя модуля 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, интерпретатор сначала ищет встроенный модуль с таким именем. Имена этих модулей перечислены в sys.builtin_module_names. Если он не найден, то в списке каталогов, заданном переменной sys.path, ищется файл с именем spam.py. Из этих мест инициализируется sys.path:

  • Каталог, содержащий входной скрипт (или текущий каталог, если файл не указан).

  • PYTHONPATH (список имен каталогов, с тем же синтаксисом, что и переменная оболочки PATH).

  • Значение по умолчанию, зависящее от установки (по соглашению включает каталог site-packages, обрабатываемый модулем site).

Более подробная информация находится по адресу Инициализация пути поиска модуля sys.path.

Примечание

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

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

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

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

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

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

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

  • Чтобы уменьшить размер скомпилированного модуля, можно использовать переключатели -O или -OO в команде Python. Переключатель -O удаляет утверждения, а переключатель -OO удаляет как утверждения, так и строки __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)  
['__breakpointhook__', '__displayhook__', '__doc__', '__excepthook__',
 '__interactivehook__', '__loader__', '__name__', '__package__', '__spec__',
 '__stderr__', '__stdin__', '__stdout__', '__unraisablehook__',
 '_clear_type_cache', '_current_frames', '_debugmallocstats', '_framework',
 '_getframe', '_git', '_home', '_xoptions', 'abiflags', 'addaudithook',
 'api_version', 'argv', 'audit', 'base_exec_prefix', 'base_prefix',
 'breakpointhook', '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', 'get_asyncgen_hooks', 'get_coroutine_origin_tracking_depth',
 'getallocatedblocks', 'getdefaultencoding', 'getdlopenflags',
 'getfilesystemencodeerrors', 'getfilesystemencoding', 'getprofile',
 'getrecursionlimit', 'getrefcount', 'getsizeof', 'getswitchinterval',
 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info',
 'intern', 'is_finalizing', 'last_traceback', 'last_type', 'last_value',
 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', 'path_hooks',
 'path_importer_cache', 'platform', 'prefix', 'ps1', 'ps2', 'pycache_prefix',
 'set_asyncgen_hooks', 'set_coroutine_origin_tracking_depth', 'setdlopenflags',
 'setprofile', 'setrecursionlimit', 'setswitchinterval', 'settrace', 'stderr',
 'stdin', 'stdout', 'thread_info', 'unraisablehook', '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 или Pillow, от необходимости беспокоиться об именах модулей друг друга.

Предположим, вы хотите разработать набор модулей («пакет») для единообразной работы со звуковыми файлами и звуковыми данными. Существует множество различных форматов звуковых файлов (обычно они распознаются по расширению, например: .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 воспринимал каталоги, содержащие этот файл, как пакеты (если только не используется namespace package, относительно продвинутая функция). Это не позволяет каталогам с общим именем, таким как 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.effects.

Помните, что подмодули могут стать теневыми из-за локально определенных имен. Например, если вы добавили функцию reverse в файл sound/effects/__init__.py, то from sound.effects import * будет импортировать только два подмодуля echo и surround, но не подмодуль reverse, потому что он затенен локально определенной функцией reverse:

__all__ = [
    "echo",      # refers to the 'echo.py' file
    "surround",  # refers to the 'surround.py' file
    "reverse",   # !!! refers to the 'reverse' function now !!!
]

def reverse(msg: str):  # <-- this name shadows the 'reverse.py' submodule
    return msg[::-1]    #     in the case of a 'from sound.effects import *'

Если __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.

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

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

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

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

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

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

Сноски