Модули
Встроенные в язык программирования функции доступны сразу. Чтобы их вызвать, не надо выполнять никаких дополнительных действий. Однако за время существования любого популярного языка на нем было написано столько функций и классов, которые оказались востребованными множеством программистов и в разных областях, что включить весь этот объем кода в сам язык если возможно, то нецелесообразно.
Чтобы разрешить проблему доступа к дополнительным возможностям языка, в программировании стало общепринятой практикой использовать так называемые модули, пакеты и библиотеки. Каждый модуль содержит коллекцию функций и классов, предназначенных для решения задач из определенной области. Так в модуле math языка Python содержатся математические функции, модуль random позволяет генерировать псевдослучайные числа, в модуле datetime содержатся классы для работы с датами и временем, модуль sys предоставляет доступ к системным переменным и т. д.
Количество модулей для языка Python огромно, что связано с популярностью языка. Часть модулей собрана в так называемую стандартную библиотеку. Стандартная она потому, что поставляется вместе с установочным пакетом. Однако существуют сторонние библиотеки. Они скачиваются и устанавливаются отдельно.
Для доступа к функционалу модуля, его надо импортировать в программу. После импорта интерпретатор «знает» о существовании дополнительных классов и функций и позволяет ими пользоваться.
В Питоне импорт осуществляется командой import . При этом существует несколько способов импорта. Рассмотрим работу с модулем на примере math . Итак,
Ничего не произошло. Однако в глобальной области видимости появилось имя math . Если до импорта вы упомянули бы имя math , то возникла бы ошибка NameError . Теперь же
В программе завелся объект math , относящийся к классу module .
Чтобы увидеть перечень функций, входящих в этот модуль, воспользуемся встроенной в Python функцией dir() , передав ей в качестве аргумента имя модуля:
Проигнорируем имена с двойными подчеркиваниями. Все остальное – имена функций и констант (переменных, которые не меняют своих значений), включенных в модуль math. Чтобы вызвать функцию из модуля, надо впереди написать имя модуля, поставить точку, далее указать имя функции, после чего в скобках передать аргументы, если они требуются. Например, чтобы вызвать функцию pow из math, надо написать так:
Обратите внимание, эта другая функция pow() , не та, что встроена в сам язык. «Обычная» функция pow() возвращает целое, если аргументы целые числа:
Для обращения к константе скобки не нужны:
Если мы не знаем, что делает та или иная функция, то можем получить справочную информацию о ней с помощью встроенной в язык Python функции help() :
Для выхода из интерактивной справки надо нажать клавишу q. В данном случае сообщается, что функция возвращает целое число, и это наибольший общий делитель для чисел x и y . Описание модулей и их содержания также можно посмотреть в официальной документации на сайте python.org.
Второй способ импорта – это когда импортируется не сам модуль, а только необходимые функции из него.
Перевести можно как «из модуля math импортировать функции gcd , sqrt и hypot «.
В таком случае при их вызове не надо перед именем функции указывать имя модуля:
Чтобы импортировать сразу все функции из модуля:
Импорт через from не лишен недостатка. В программе уже может быть идентификатор с таким же именем, как имя одной из импортируемых функций или констант. Ошибки не будет, но одно из них окажется «затерто»:
Здесь исчезает значение 3.14, присвоенное переменной pi . Это имя теперь указывает на число из модуля math . Если импорт сделать раньше, чем присвоение значения pi , то будет все наоборот:
В этой связи более опасен именно импорт всех функций. Так как в этом случае очень легко не заметить подмены значений идентификаторов.
Однако можно изменить имя идентификатора из модуля на какое угодно:
В данном случае константа pi из модуля импортируется под именем P . Смысл подобных импортов в сокращении имен, так как есть модули с длинными именами, а имена функций и классов в них еще длиннее. Если в программу импортируется всего пара сущностей, и они используются в ней часто, то имеет смысл переименовать их на более короткий вариант. Сравните:
Во всех остальных случаях лучше оставлять идентификаторы содержимого модуля в пространстве имен самого модуля и получать доступ к ним через имя модуля, то есть выполнять импорт командой import имя_модуля , а вызывать, например, функции через имя_модуля.имя_функции() .
Практическая работа. Создание собственного модуля
Программист на Python всегда может создать собственный модуль, чтобы использовать его в нескольких своих программах или даже предоставить в пользование всему миру. В качестве тренировки создадим модуль с функциями для вычисления площадей прямоугольника, треугольника и круга:
Здесь также иллюстрируется принцип, что один модуль может импортировать другие. В данном случае импортируются функции из модуля math .
Поместите данный код в отдельный файл square.py . Однако куда поместить сам файл?
Когда интерпретатор Питона встречает команду импорта, то просматривает на наличие файла-модуля определенные каталоги. Их перечень можно увидеть по содержимому sys.path :
Это список адресов в Linux. В Windows он будет несколько другим. Первый элемент – пустая строка, что обозначает текущий каталог, то есть то место, где сохранена сама программа, импортирующая модуль. Если вы сохраните файл-модуль и файл-программу в одном каталоге, то интерпретатор без труда найдет модуль.
Также модуль можно положить в любой другой из указанных в списке каталогов. Тогда он будет доступен для всех программ на Python, а также его можно будет импортировать в интерактивном режиме.
Можно добавить в sys.path свой каталог. Однако в этом случае либо код программы должен содержать команды изменения значения sys.path , либо надо править конфигурационный файл операционной системы. В большинстве случаев лучше так не делать.
Поместите файл square.py в тот же каталог, где будет исполняемая программа. Ее код должен включать инструкцию импорта модуля square (при импорте расширение файла не указывается) и вызов той функции и с теми параметрами, которые ввел пользователь. То есть у пользователя надо спросить, площадь какой фигуры он хочет вычислить. Далее запросить у него аргументы для соответствующей функции. Передать их в функцию из модуля square , а полученный оттуда результат вывести на экран.
Примечание. Исполнение модуля как самостоятельного скрипта, а также создание строк документации, которые отображает встроенная в Python функция help() , будут рассмотрены в курсе объектно-ориентированного программирования.
Примеры решения и дополнительные уроки в android-приложении и pdf-версии курса
Модули, введение
По мере роста нашего приложения, мы обычно хотим разделить его на много файлов, так называемых «модулей». Модуль обычно содержит класс или библиотеку с функциями.
Долгое время в JavaScript отсутствовал синтаксис модулей на уровне языка. Это не было проблемой, потому что первые скрипты были маленькими и простыми. В модулях не было необходимости.
Но со временем скрипты становились всё более и более сложными, поэтому сообщество придумало несколько вариантов организации кода в модули. Появились библиотеки для динамической подгрузки модулей.
-
– одна из самых старых модульных систем, изначально реализована библиотекой require.js. – модульная система, созданная для сервера Node.js. – ещё одна модульная система, предлагается как универсальная, совместима с AMD и CommonJS.
Теперь все они постепенно становятся частью истории, хотя их и можно найти в старых скриптах.
Система модулей на уровне языка появилась в стандарте JavaScript в 2015 году и постепенно эволюционировала. На данный момент она поддерживается большинством браузеров и Node.js. Далее мы будем изучать именно её.
Что такое модуль?
Модуль – это просто файл. Один скрипт – это один модуль.
Модули могут загружать друг друга и использовать директивы export и import , чтобы обмениваться функциональностью, вызывать функции одного модуля из другого:
- export отмечает переменные и функции, которые должны быть доступны вне текущего модуля.
- import позволяет импортировать функциональность из других модулей.
Например, если у нас есть файл sayHi.js , который экспортирует функцию:
…Тогда другой файл может импортировать её и использовать:
Директива import загружает модуль по пути ./sayHi.js относительно текущего файла и записывает экспортированную функцию sayHi в соответствующую переменную.
Давайте запустим пример в браузере.
Так как модули поддерживают ряд специальных ключевых слов, и у них есть ряд особенностей, то необходимо явно сказать браузеру, что скрипт является модулем, при помощи атрибута <script type="module"> .
Браузер автоматически загрузит и запустит импортированный модуль (и те, которые он импортирует, если надо), а затем запустит скрипт.
Если вы попытаетесь открыть веб-страницу локально, через протокол file:// , вы обнаружите, что директивы import/export не работают. Для тестирования модулей используйте локальный веб-сервер, например, static-server или используйте возможности «живого сервера» вашего редактора, например, расширение Live Server для VS Code.
Основные возможности модулей
Чем отличаются модули от «обычных» скриптов?
Есть основные возможности и особенности, работающие как в браузере, так и в серверном JavaScript.
Всегда «use strict»
В модулях всегда используется режим use strict . Например, присваивание к необъявленной переменной вызовет ошибку.
Своя область видимости переменных
Каждый модуль имеет свою собственную область видимости. Другими словами, переменные и функции, объявленные в модуле, не видны в других скриптах.
В следующем примере импортированы 2 скрипта, и hello.js пытается использовать переменную user , объявленную в user.js . В итоге ошибка:
Модули должны экспортировать функциональность, предназначенную для использования извне. А другие модули могут её импортировать.
Так что нам надо импортировать user.js в hello.js и взять из него нужную функциональность, вместо того чтобы полагаться на глобальные переменные.
В браузере также существует независимая область видимости для каждого скрипта <script type="module"> :
Если нам нужно сделать глобальную переменную уровня всей страницы, можно явно присвоить её объекту window , тогда получить значение переменной можно обратившись к window.user . Но это должно быть исключением, требующим веской причины.
Код в модуле выполняется только один раз при импорте
Если один и тот же модуль используется в нескольких местах, то его код выполнится только один раз, после чего экспортируемая функциональность передаётся всем импортёрам.
Это очень важно для понимания работы модулей. Давайте посмотрим примеры.
Во-первых, если при запуске модуля возникают побочные эффекты, например выдаётся сообщение, то импорт модуля в нескольких местах покажет его только один раз – при первом импорте:
На практике, задача кода модуля – это обычно инициализация, создание внутренних структур данных, а если мы хотим, чтобы что-то можно было использовать много раз, то экспортируем это.
Теперь более продвинутый пример.
Давайте представим, что модуль экспортирует объект:
Если модуль импортируется в нескольких файлах, то код модуля будет выполнен только один раз, объект admin будет создан и в дальнейшем будет передан всем импортёрам.
Все импортёры получат один-единственный объект admin :
Ещё раз заметим – модуль выполняется только один раз. Генерируется экспорт и после передаётся всем импортёрам, поэтому, если что-то изменится в объекте admin , то другие модули тоже увидят эти изменения.
Такое поведение позволяет конфигурировать модули при первом импорте. Мы можем установить его свойства один раз, и в дальнейших импортах он будет уже настроенным.
Например, модуль admin.js предоставляет определённую функциональность, но ожидает передачи учётных данных в объект admin извне:
В init.js , первом скрипте нашего приложения, мы установим admin.name . Тогда все это увидят, включая вызовы, сделанные из самого admin.js :
Другой модуль тоже увидит admin.name :
import.meta
Объект import.meta содержит информацию о текущем модуле.
Содержимое зависит от окружения. В браузере он содержит ссылку на скрипт или ссылку на текущую веб-страницу, если модуль встроен в HTML:
В модуле «this» не определён
Это незначительная особенность, но для полноты картины нам нужно упомянуть об этом.
В модуле на верхнем уровне this не определён (undefined).
Сравним с не-модульными скриптами, там this – глобальный объект:
Особенности в браузерах
Есть и несколько других, именно браузерных особенностей скриптов с type="module" по сравнению с обычными скриптами.
Если вы читаете материал в первый раз или, если не собираетесь использовать модули в браузерах, то сейчас можете пропустить эту секцию.
Модули являются отложенными (deferred)
Модули всегда выполняются в отложенном (deferred) режиме, точно так же, как скрипты с атрибутом defer (описан в главе Скрипты: async, defer). Это верно и для внешних и встроенных скриптов-модулей.
- загрузка внешних модулей, таких как <script type="module" src=". "> , не блокирует обработку HTML.
- модули, даже если загрузились быстро, ожидают полной загрузки HTML документа, и только затем выполняются.
- сохраняется относительный порядок скриптов: скрипты, которые идут раньше в документе, выполняются раньше.
Как побочный эффект, модули всегда видят полностью загруженную HTML-страницу, включая элементы под ними.
Пожалуйста, обратите внимание: второй скрипт выполнится раньше, чем первый! Поэтому мы увидим сначала undefined , а потом object .
Это потому, что модули начинают выполняться после полной загрузки страницы. Обычные скрипты запускаются сразу же, поэтому сообщение из обычного скрипта мы видим первым.
При использовании модулей нам стоит иметь в виду, что HTML-страница будет показана браузером до того, как выполнятся модули и JavaScript-приложение будет готово к работе. Некоторые функции могут ещё не работать. Нам следует разместить «индикатор загрузки» или что-то ещё, чтобы не смутить этим посетителя.
Атрибут async работает во встроенных скриптах
Для не-модульных скриптов атрибут async работает только на внешних скриптах. Скрипты с ним запускаются сразу по готовности, они не ждут другие скрипты или HTML-документ.
Для модулей атрибут async работает на любых скриптах.
Например, в скрипте ниже есть async , поэтому он выполнится сразу после загрузки, не ожидая других скриптов.
Скрипт выполнит импорт (загрузит ./analytics.js ) и сразу запустится, когда будет готов, даже если HTML документ ещё не будет загружен, или если другие скрипты ещё загружаются.
Это очень полезно, когда модуль ни с чем не связан, например для счётчиков, рекламы, обработчиков событий.
Внешние скрипты
Внешние скрипты с атрибутом type="module" имеют два отличия:
Внешние скрипты с одинаковым атрибутом src запускаются только один раз:
Внешний скрипт, который загружается с другого домена, требует указания заголовков CORS. Другими словами, если модульный скрипт загружается с другого домена, то удалённый сервер должен установить заголовок Access-Control-Allow-Origin означающий, что загрузка скрипта разрешена.
Это обеспечивает лучшую безопасность по умолчанию.
Не допускаются «голые» модули
В браузере import должен содержать относительный или абсолютный путь к модулю. Модули без пути называются «голыми» (bare). Они не разрешены в import .
Например, этот import неправильный:
Другие окружения, например Node.js, допускают использование «голых» модулей, без путей, так как в них есть свои правила, как работать с такими модулями и где их искать. Но браузеры пока не поддерживают «голые» модули.
Совместимость, «nomodule»
Старые браузеры не понимают атрибут type="module" . Скрипты с неизвестным атрибутом type просто игнорируются. Мы можем сделать для них «резервный» скрипт при помощи атрибута nomodule :
Инструменты сборки
В реальной жизни модули в браузерах редко используются в «сыром» виде. Обычно, мы объединяем модули вместе, используя специальный инструмент, например Webpack и после выкладываем код на рабочий сервер.
Одно из преимуществ использования сборщика – он предоставляет больший контроль над тем, как модули ищутся, позволяет использовать «голые» модули и многое другое «своё», например CSS/HTML-модули.
Сборщик делает следующее:
- Берёт «основной» модуль, который мы собираемся поместить в <script type="module"> в HTML.
- Анализирует зависимости (импорты, импорты импортов и так далее)
- Собирает один файл со всеми модулями (или несколько файлов, это можно настроить), перезаписывает встроенный import функцией импорта от сборщика, чтобы всё работало. «Специальные» типы модулей, такие как HTML/CSS тоже поддерживаются.
- В процессе могут происходить и другие трансформации и оптимизации кода:
- Недостижимый код удаляется.
- Неиспользуемые экспорты удаляются («tree-shaking»).
- Специфические операторы для разработки, такие как console и debugger , удаляются.
- Современный синтаксис JavaScript также может быть трансформирован в предыдущий стандарт, с похожей функциональностью, например, с помощью Babel.
- Полученный файл можно минимизировать (удалить пробелы, заменить названия переменных на более короткие и т.д.).
Если мы используем инструменты сборки, то они объединяют модули вместе в один или несколько файлов, и заменяют import/export на свои вызовы. Поэтому итоговую сборку можно подключать и без атрибута type="module" , как обычный скрипт:
Хотя и «как есть» модули тоже можно использовать, а сборщик настроить позже при необходимости.
Итого
Подводя итог, основные понятия:
- Модуль – это файл. Чтобы работал import/export , нужно для браузеров указывать атрибут <script type="module"> . У модулей есть ряд особенностей:
- Отложенное (deferred) выполнение по умолчанию.
- Атрибут async работает во встроенных скриптах.
- Для загрузки внешних модулей с другого источника, он должен ставить заголовки CORS.
- Дублирующиеся внешние скрипты игнорируются.
- У модулей есть своя область видимости, обмениваться функциональностью можно через import/export .
- В модулях всегда включена директива use strict .
- Код в модулях выполняется только один раз. Экспортируемая функциональность создаётся один раз и передаётся всем импортёрам.
Когда мы используем модули, каждый модуль реализует свою функциональность и экспортирует её. Затем мы используем import , чтобы напрямую импортировать её туда, куда необходимо. Браузер загружает и анализирует скрипты автоматически.
В реальной жизни часто используется сборщик Webpack, чтобы объединить модули: для производительности и других «плюшек».
В следующей главе мы увидим больше примеров и вариантов импорта/экспорта.
№21 Модули Python / Уроки по Python для начинающих
Модуль — это файл, содержащий код python, который вы хотите включить в проект.
Документацию по модулям python на русском мы собрали в разделе Модули.
Создание модуля
Для того, чтобы создать модуль достаточно просто сохранить код в файл с расширением .py :
Сохраним этот код в файл под названием mymodule.py
Использование модуля
Теперь мы можем использовать только что созданный модуль, с помощью оператора import :
Импортируем модуль под названием mymodule, и вызовем функцию приветствия:
Примечание: Во время использования функции из модуля, синтаксис: module_name.function_name .
Переменные в модуле
Модуль может содержать функции, как уже описано, но также и переменные всех типов (массивы, словари, объекты и т. д.).
Сохраним этот код в файл mymodule.py
Импортируем модуль с названием mymodule, и получим доступ к словарю person1 :
Имя модуля
Вы можете назвать файл модуля, как вам нравится, но важно, указать расширение файла .py
Переименование модуля
Вы можете создать псевдоним при импорте модуля, используя ключевое слово as .
Встроенные модули
В Python есть несколько встроенных модулей, которые вы можете импортировать, когда захотите.
Использование функции dir()
Существует встроенная функция для перечисления всех имен функций (или имен переменных) в модуле. Функция dir() .
Примечание: Функцию dir() можно использовать на всех модулях, включая те, которые вы создаете сами.
Импорт из модуля
Вы можете импортировать модуль только частично, используя ключевое слово from