6. Модули

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

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

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

# модуль чисел Фибоначчи
 
def fib(n):    # выводит ряд Фибоначчи до n
    a, b = 0, 1
    while b < n:
        print(b, end=' ')
        a, b = b, a+b
    print()
 
def fib2(n): # возвращает ряд Фибоначчи до n
    result = []
    a, b = 0, 1
    while b < n:
        result.append(b)
        a, b = b, a+b
    return result

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

>>> import fibo

Это не вводит имена определенных в 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

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

Наряду с определениями функций модули могут содержать исполняемые операторы. Эти операторы предназначены для инициализации модуля. Они выполняются только однажды, когда в операторе импорта встречается имя модуля. [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 не используют такой импорт, поскольку он вводит неизвестное множество имен в интерпретатор, возможно скрытие нескольких объектов, которые вы уже определили.

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

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

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

Когда вы запускаете модуль 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
>>>

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

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

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

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

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

После инициализация программа на Python может изменять sys.path. Директория, содержащая запущенный скрипт, размещается в начале пути поиска, впереди пути стандартной библиотеки. Это означает, что скрипты из данной директории будут загружены вместо модулей с такими же именами в каталоге библиотеки. Если замена не предполагалась, то это ошибка. См. раздел Standard Modules (docs.python.org/3/tutorial/modules.html#tut-standardmodules) для большей информации.

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

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

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

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

Некоторые советы для специалистов:

  • Вы можете использовать переключатели -O и -OO в командах Python для уменьшения размера скомпилированного модуля. Переключатель -O удаляет операторы, переключатель -OO удаляет операторы и строки __doc__. Поскольку некоторые программы могут рассчитывать, что все это доступно, вам следует использовать эту опцию, если вы знаете, что делаете. У "оптимизированного" модуля суффикс .pyo вместо .pyc, и он обычно меньше. Будущие релизы могут изменить эффекты оптимизации.
  • Программа не работает быстрее, когда читается из файлов .pyc или .pyo, чем когда она читается из файла .py; единственное, что быстрее для файлов .pyc или .pyo - это скорость с которой они загружаются.
  • Модуль compileall может создавать файлы .pyc (или .pyo при использовании -O) для всех модулей в каталоге.
  • Есть больше информации по этому процессу, включая блок-схему решения, в PEP 3147.

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

Python поставляется с библиотекой стандартных модулей, описанных в отдельном документе, Справка по библиотеки Python (ниже "Справка по библиотеке"). Несколько модулей встроено в интерпретатор; они предоставляют доступ к операциям, которые не являются частью ядра языка, но тем не менее встроены в него, либо для повышения эффективности, либо для обеспечения доступа к примитивам операционной системы, таким как системные вызовы. Установка таких модулей является конфигурационной опцией, которая также зависит от используемой платформы. Например, модуль winreg (docs.python.org/3/library/winreg.html#module-winreg) предоставляется только на системах Windows. Один особый модуль заслуживает внимания: sys (docs.python.org/3/library/sys.html#module-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 или Python Imaging Library, не беспокоиться об именах модулей друг друга.

Предположим, вы хотите разработать коллекцию модулей ("пакет") для единообразной обработки звуковых файлов и звуковых данных. Существует множество различных форматов звуковых файлов (обычно распознаваемых по их расширению, например: .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, item может быть либо подмодулем (или подпакетом) пакета, либо каким-нибудь другим именем, определенным в пакете, как функция, класс или переменная. Оператор import сначала проверяет, есть ли определенный элемент в пакете; если нет, он предполагает, что это модуль и пытается загрузить его. Если он не находится, возбуждается исключение ImportError.

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

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

Теперь что случится, когда пользователь напишет 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. Intra-package ссылки (справки, рекомендации)

Когда пакеты структурированы в подпакеты (как в пакете 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 пакета, перед кодом в котором файл выполняется. Эта переменная может быть изменена; оказывая таким образом влияние на будущий поиск модулей и подпакетов, включенных в пакет.

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

Примечания

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

Создано