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. Если он не найден, то производится поиск файла с именем spam.py в списке каталогов, заданном переменной sys.path. sys.path инициализируется из этих мест:

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

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

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

Примечание

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

После инициализации программы 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 рассматривал каталоги, содержащие этот файл, как пакеты. Это не позволяет каталогам с общим именем, таким как 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.

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

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

Вернуться на верх