6. Модули¶
Если вы выйдете из интерпретатора Python и войдёте в него снова, сделанные вами определения (функции и переменные) будут потеряны. Поэтому, если вы хотите написать более длинную программу, вам лучше использовать текстовый редактор, чтобы подготовить ввод для интерпретатора, и запустить его с этим файлом в качестве входных данных. Это называется созданием скрипта. По мере того, как ваша программа становится длиннее, вы можете разделить ее на несколько файлов для упрощения обслуживания. Вы также можете использовать удобную функцию, которую вы написали в нескольких программах, не копируя ее определение в каждую программу.
Чтобы поддержать это, в Python есть способ поместить определения в файл и использовать их в скрипте или в интерактивном экземпляре интерпретатора. Такой файл называется модулем; определения из модуля могут быть импортированы в другие модули или в главный модуль (набор переменных, к которым у вас есть доступ в скрипте, выполняемом на верхнем уровне и в режиме калькулятора).
Модуль — это файл, содержащий определения и инструкции Python. Имя файла — это имя модуля с суффиксом .py. Внутри модуля его имя (в виде строки) доступно как значение глобальной переменной __name__. Например, используйте ваш любимый текстовый редактор, чтобы создать файл с именем fibo.py в текущем каталоге со следующим содержимым:
# Модуль чисел Фибоначчи
def fib(n):
"""Вывести ряд Фибоначчи до n."""
a, b = 0, 1
while a < n:
print(a, end=' ')
a, b = b, a+b
print()
def fib2(n):
"""Вернуть ряд Фибоначчи до n."""
result = []
a, b = 0, 1
while a < n:
result.append(a)
a, b = b, a+b
return result
Теперь запустите интерпретатор Python и импортируйте этот модуль с помощью следующей команды:
>>> import fibo
Эта команда не добавляет имена функций, определенных в fibo, непосредственно в текущее пространство имён (подробности см. Области видимости и пространства имён в 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(имя модуля).
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).Значение по умолчанию, определяемое способом установки Python (обычно включает каталог
site-packages, который обрабатывается модулемsite).
Более подробная информация приведена в The initialization of the sys.path module search 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удаляет инструкции assert, а-OOудаляет и assert, и строки __doc__. Поскольку некоторые программы могут полагаться на их наличие, использовать эти опции стоит только если вы точно понимаете последствия. «Оптимизированные» модули имеют тегopt-и обычно меньше по размеру. В будущих выпусках поведение оптимизации может измениться.Программа не работает быстрее при запуске из файла
.pyc, чем при запуске из.py. Отличие только в том, что файлы.pycзагружаются быстрее.Модуль
compileallможет создать файлы .pyc для всех модулей в каталоге.Подробности этого процесса, включая блок-схему принятия решений, приведены в PEP 3147.
6.2. Стандартные модули¶
Python поставляется с библиотекой стандартных модулей, описанной в отдельном документе «Справочник по библиотеке Python» («Справочник по библиотеке» далее). Некоторые модули встроены в интерпретатор; они обеспечивают доступ к операциям, которые не являются частью ядра языка, но тем не менее встроены — либо для повышения эффективности, либо для обеспечения доступа к примитивам операционной системы, таким как системные вызовы. Набор таких модулей является опцией конфигурации, а также зависит от базовой платформы. Например, модуль winreg предоставляется только в системах Windows. Отдельного внимания заслуживает один модуль: sys, который встроен в каждый интерпретатор Python. Переменные sys.ps1 и sys.ps2 определяют строки, используемые в качестве основного и вторичного приглашений:
>>> import sys
>>> sys.ps1
'>>> '
>>> sys.ps2
'... '
>>> sys.ps1 = 'C> '
C> print('Фу!')
Фу!
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/ Пакет верхнего уровня
__init__.py Инициализация пакета sound
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. Импорт * из пакета¶
Что же происходит, когда пользователь пишет 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", # ссылается на файл 'echo.py'
"surround", # ссылается на файл 'surround.py'
"reverse", # !!! теперь ссылается на функцию 'reverse'!!!
]
def reverse(msg: str): # <-- это имя скрывает подмодуль 'reverse.py'
return msg[::-1] # в случае '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. Такие импорты используют ведущие точки для указания текущего и родительских пакетов, участвующих в относительном импорте. Например, в модуле surround вы можете написать:
from . import echo
from .. import formats
from ..filters import equalizer
Обратите внимание, что относительные импорты основываются на имени пакета текущего модуля. Поскольку у главного модуля нет пакета, модули, предназначенные для использования в качестве главного модуля Python-приложения, всегда должны использовать абсолютные импорты.
6.4.3. Пакеты в нескольких каталогах¶
Пакеты поддерживают ещё один специальный атрибут: __path__. Он инициализируется как последовательность строк, содержащих имя каталога, в котором находится файл __init__.py пакета, прежде чем код в этом файле будет выполнен. Эту переменную можно изменить; это повлияет на будущий поиск модулей и подпакетов, содержащихся в пакете.
Хотя эта возможность требуется нечасто, она может быть использована для расширения набора модулей, доступных в пакете.
Примечания