Обзор основ модулей

Beginner

This tutorial is from open-source community. Access the source code

Введение

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

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

Создание простого модуля

Начнем наше знакомство с модулями Python с создания простого модуля. В Python модуль — это по сути файл с расширением .py, содержащий код на Python. Представьте его как контейнер, в котором можно сгруппировать связанные функции, классы и переменные. Это делает ваш код более организованным и легким для управления, особенно по мере роста размеров проектов.

  1. Сначала откройте WebIDE. После открытия вам нужно создать новый файл. Для этого нажмите на "File" в меню и выберите "New File". Назовите этот новый файл simplemod.py и сохраните его в директории /home/labex/project. В этой директории мы будем хранить все файлы, связанные с этим экспериментом.

  2. Теперь добавим некоторый код в только что созданный файл simplemod.py. Ниже приведен код, который определяет несколько базовых элементов, которые обычно можно найти в модуле Python.

## simplemod.py

x = 42        ## Глобальная переменная

## Простая функция
def foo():
    print('x is', x)

## Простой класс
class Spam:
    def yow(self):
        print('Yow!')

## Скрипт-инструкция
print('Loaded simplemod')

В этом коде:

  • x = 42 создает глобальную переменную с именем x и присваивает ей значение 42. Глобальные переменные могут быть доступны из любого места в модуле.
  • Функция foo() определена для вывода значения глобальной переменной x. Функции — это повторно используемые блоки кода, выполняющие определенную задачу.
  • Класс Spam является шаблоном для создания объектов. Он имеет метод с именем yow(), который просто выводит строку 'Yow!'. Методы — это функции, принадлежащие классу.
  • Инструкция print('Loaded simplemod') является скрипт-инструкцией. Она выполнится сразу после загрузки модуля, что помогает нам убедиться, что модуль успешно загружен.
  1. После добавления кода сохраните файл. Вы можете сделать это, нажав Ctrl+S на клавиатуре или выбрав "File" > "Save" в меню. Сохранение файла гарантирует, что все изменения, которые вы внесли, будут сохранены.

Давайте более подробно рассмотрим, что содержит этот модуль:

  • Глобальная переменная x со значением 42. Эта переменная может быть использована в любом месте модуля и даже доступна из других модулей при правильном импорте.
  • Функция foo(), которая выводит значение x. Функции полезны для выполнения повторяющихся задач без необходимости многократно писать один и тот же код.
  • Класс Spam с методом yow(). Классы и методы — это фундаментальные концепции объектно-ориентированного программирования, которые позволяют создавать сложные структуры данных и поведения.
  • Инструкция print, которая выполняется при загрузке модуля. Эта инструкция служит визуальным индикатором того, что модуль успешно загружен в среду Python.

Инструкция print внизу поможет нам наблюдать, когда модуль загружается, что важно для отладки и понимания того, как работают модули в Python.

Импорт и использование модулей

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

  1. Сначала нам нужно открыть новый терминал в WebIDE. Этот терминал станет нашим рабочим пространством, где мы можем запускать команды Python. Чтобы открыть новый терминал, нажмите "Terminal" > "New Terminal".

  2. После открытия терминала нам нужно запустить интерпретатор Python. Интерпретатор Python — это программа, которая читает и выполняет код на Python. Чтобы запустить его, введите следующую команду в терминале и нажмите Enter:

python3
  1. Теперь, когда интерпретатор Python запущен, мы можем импортировать наш модуль. В Python мы используем инструкцию import для подключения модуля к нашей текущей программе. Введите следующую команду в интерпретаторе Python:
>>> import simplemod
Loaded simplemod

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

  1. После импорта модуля мы можем обращаться к его компонентам с помощью точечной нотации. Точечная нотация — это способ обращения к атрибутам (переменным и функциям) объекта в Python. В данном случае модуль является объектом, а его функции, переменные и классы — его атрибутами. Вот несколько примеров того, как обращаться к различным компонентам модуля simplemod:
>>> simplemod.x
42
>>> simplemod.foo()
x is 42
>>> spam_instance = simplemod.Spam()
>>> spam_instance.yow()
Yow!

В первой строке мы обращаемся к переменной x, определенной в модуле simplemod. Во второй строке мы вызываем функцию foo из модуля simplemod. В третьей и четвертой строках мы создаем экземпляр класса Spam, определенного в модуле simplemod, и вызываем его метод yow.

  1. Иногда при попытке импортировать модуль вы можете столкнуться с ошибкой ImportError. Эта ошибка возникает, когда Python не может найти модуль, который вы пытаетесь импортировать. Чтобы понять, где Python ищет модули, вы можете проверить переменную sys.path. Переменная sys.path представляет собой список директорий, которые Python просматривает при поиске модулей. Введите следующие команды в интерпретаторе Python:
>>> import sys
>>> sys.path
['', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

Первый элемент в списке (пустая строка) представляет текущую рабочую директорию. Именно здесь Python ищет файл simplemod.py. Если ваш модуль не находится в одной из директорий, перечисленных в sys.path, Python не сможет его найти, и вы получите ошибку ImportError. Убедитесь, что файл simplemod.py находится в текущей рабочей директории или в одной из других директорий в sys.path.

Понимание поведения загрузки модулей

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

  1. Сначала посмотрим, что происходит, когда мы пытаемся импортировать модуль снова в рамках одной сессии интерпретатора Python. Когда вы запускаете интерпретатор Python, это похоже на открытие рабочего пространства, где вы можете выполнять код на Python. После того, как вы импортировали модуль, повторный импорт может показаться, что он перезагрузит модуль, но это не так.
>>> import simplemod

Обратите внимание, что на этот раз вы не видите вывод "Loaded simplemod". Это потому, что Python загружает модуль только один раз за сессию интерпретатора. Последующие инструкции import не перезагружают модуль. Python запоминает, что он уже загрузил модуль, поэтому не повторяет процесс загрузки.

  1. После импорта модуля вы можете изменить переменные внутри него. Модуль в Python похож на контейнер, который хранит переменные, функции и классы. После импорта модуля вы можете обращаться к его переменным и изменять их, как и с любым другим объектом Python.
>>> simplemod.x
42
>>> simplemod.x = 13
>>> simplemod.x
13
>>> simplemod.foo()
x is 13

Здесь мы сначала проверяем значение переменной x в модуле simplemod, которое изначально равно 42. Затем мы изменяем его значение на 13 и убеждаемся, что изменение произошло. Когда мы вызываем функцию foo в модуле, она отражает новое значение x.

  1. Повторный импорт модуля не сбрасывает изменения, которые мы внесли в его переменные. Даже если мы попытаемся импортировать модуль еще раз, Python не перезагружает его, поэтому изменения, которые мы внесли в его переменные, остаются.
>>> import simplemod
>>> simplemod.x
13
  1. Если вы хотите принудительно перезагрузить модуль, вам нужно использовать функцию importlib.reload(). Иногда вы можете внести изменения в код модуля и хотите, чтобы эти изменения вступили в силу сразу. Функция importlib.reload() позволяет сделать именно это.
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
>>> simplemod.x
42
>>> simplemod.foo()
x is 42

Модуль был перезагружен, и значение x было сброшено до 42. Это показывает, что модуль был загружен снова из его исходного кода, и все переменные были инициализированы, как изначально.

  1. Python отслеживает все загруженные модули в словаре sys.modules. Этот словарь действует как реестр, где Python хранит информацию о всех модулях, которые были загружены в текущей сессии интерпретатора.
>>> 'simplemod' in sys.modules
True
>>> sys.modules['simplemod']
<module 'simplemod' from 'simplemod.py'>

Проверяя, находится ли имя модуля в словаре sys.modules, вы можете узнать, был ли модуль загружен. И обратившись к словарю с именем модуля в качестве ключа, вы можете получить информацию о модуле.

  1. Вы можете удалить модуль из этого словаря, чтобы заставить Python перезагрузить его при следующем импорте. Если вы удалите модуль из словаря sys.modules, Python забудет, что он уже загрузил модуль. Поэтому в следующий раз, когда вы попытаетесь импортировать его, Python загрузит его снова из исходного кода.
>>> del sys.modules['simplemod']
>>> import simplemod
Loaded simplemod
>>> simplemod.x
42

Модуль был загружен снова, потому что он был удален из sys.modules. Это еще один способ убедиться, что вы работаете с последней версией кода модуля.

Использование синтаксиса from module import

В Python существует несколько способов импортировать компоненты из модулей. Одним из них является синтаксис from module import, который мы рассмотрим в этом разделе.

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

  1. Перезапустите интерпретатор Python, чтобы получить чистый статус:
>>> exit()

Эта команда завершает текущую сессию интерпретатора Python. После выхода мы запустим новую сессию, чтобы обеспечить свежее окружение.

python3

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

  1. Импортируйте конкретные компоненты из модуля, используя синтаксис from module import:
>>> from simplemod import foo
Loaded simplemod
>>> foo()
x is 42

Здесь мы используем инструкцию from simplemod import foo для импорта только функции foo из модуля simplemod. Обратите внимание, что даже несмотря на то, что мы запросили только функцию foo, весь модуль simplemod был загружен. Это подтверждается выводом "Loaded simplemod". Причина этого заключается в том, что Python должен загрузить весь модуль, чтобы получить доступ к функции foo.

  1. При использовании from module import вы не можете обращаться к самому модулю:
>>> simplemod.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'simplemod' is not defined

Когда мы используем синтаксис from module import, мы напрямую импортируем только указанные компоненты в наше пространство имен. Само имя модуля не импортируется. Поэтому, когда мы пытаемся обратиться к simplemod.foo(), Python не распознает simplemod, так как он не был импортирован таким образом.

  1. Вы можете импортировать несколько компонентов сразу:
>>> from simplemod import x, foo
>>> x
42
>>> foo()
x is 42

Синтаксис from module import позволяет нам импортировать несколько компонентов из модуля в одной инструкции. Здесь мы импортируем как переменную x, так и функцию foo из модуля simplemod. После импорта мы можем напрямую обращаться к этим компонентам в нашем коде.

  1. Когда вы импортируете переменную из модуля, вы создаете новую ссылку на объект, а не ссылку на переменную в модуле:
>>> x = 13  ## Change the local variable x
>>> x
13
>>> foo()
x is 42  ## The function still uses the module's x, not your local x

Когда мы импортируем переменную из модуля, мы по сути создаем новую ссылку на тот же объект в нашем локальном пространстве имен. Поэтому, когда мы изменяем локальную переменную x на 13, это не влияет на переменную x внутри модуля simplemod. Функция foo() по-прежнему ссылается на переменную x модуля, которая равна 42. Понимание этого концепта является важным для избежания путаницы в вашем коде.

Исследование ограничений перезагрузки модулей

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

  1. Перезапустите интерпретатор Python: Сначала нам нужно перезапустить интерпретатор Python. Этот шаг важен, так как он гарантирует, что мы начинаем с "чистого листа". При перезапуске интерпретатора все ранее импортированные модули и переменные очищаются. Чтобы выйти из текущего интерпретатора Python, используйте команду exit(). Затем запустите новую сессию интерпретатора Python с помощью команды python3 в терминале.
>>> exit()
python3
  1. Импортируйте модуль и создайте экземпляр класса Spam: Теперь, когда у нас есть новая сессия интерпретатора Python, мы импортируем модуль simplemod. Импорт модуля позволяет нам использовать классы, функции и переменные, определенные в этом модуле. После импорта модуля мы создадим экземпляр класса Spam и вызовем его метод yow(). Это поможет нам увидеть первоначальное поведение класса.
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Yow!
  1. Теперь изменим класс Spam в нашем модуле. Выйдите из интерпретатора Python: Далее мы собираемся внести изменения в класс Spam в модуле simplemod. Перед этим нам нужно выйти из интерпретатора Python. Это связано с тем, что мы хотим внести изменения в исходный код модуля и затем увидеть, как эти изменения влияют на поведение класса.
>>> exit()
  1. Откройте файл simplemod.py в WebIDE и измените класс Spam: Откройте файл simplemod.py в WebIDE. Именно здесь находится исходный код модуля simplemod. Мы изменим метод yow() класса Spam так, чтобы он выводил другое сообщение. Это изменение поможет нам наблюдать, как поведение класса меняется после перезагрузки модуля.
## simplemod.py
## ... (оставьте остальную часть файла без изменений)

class Spam:
    def yow(self):
        print('More Yow!')  ## Changed from 'Yow!'
  1. Сохраните файл и вернитесь в терминал. Запустите интерпретатор Python и создайте новый экземпляр: После внесения изменений в файл simplemod.py сохраните его. Затем вернитесь в терминал и запустите новую сессию интерпретатора Python. Импортируйте модуль simplemod снова и создайте новый экземпляр класса Spam. Вызовите метод yow() нового экземпляра, чтобы увидеть обновленное поведение.
python3
>>> import simplemod
Loaded simplemod
>>> t = simplemod.Spam()
>>> t.yow()
More Yow!
  1. Теперь продемонстрируем, что происходит при перезагрузке: Чтобы увидеть, как работает перезагрузка модуля, мы используем функцию importlib.reload(). Эта функция позволяет нам перезагрузить ранее импортированный модуль. После перезагрузки модуля мы проверим, отражаются ли изменения, которые мы внесли в класс Spam.
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>
  1. Выйдите из Python, измените файл еще раз и протестируйте оба экземпляра: Выйдите из интерпретатора Python еще раз. Затем внесите еще одно изменение в класс Spam в файле simplemod.py. После этого мы протестируем как старый, так и новый экземпляры класса Spam, чтобы увидеть, как они ведут себя.
>>> exit()
  1. Обновите файл simplemod.py: Откройте файл simplemod.py снова и измените метод yow() класса Spam так, чтобы он выводил другое сообщение. Это изменение поможет нам лучше понять ограничения перезагрузки модулей.
## simplemod.py
## ... (оставьте остальную часть файла без изменений)

class Spam:
    def yow(self):
        print('Even More Yow!')  ## Changed again
  1. Сохраните файл и вернитесь в терминал: Сохраните изменения в файле simplemod.py и вернитесь в терминал. Запустите новую сессию интерпретатора Python, импортируйте модуль simplemod и создайте новый экземпляр класса Spam. Вызовите метод yow() нового экземпляра, чтобы увидеть обновленное поведение.
python3
>>> import simplemod
Loaded simplemod
>>> s = simplemod.Spam()
>>> s.yow()
Even More Yow!

>>> ## Exit without closing Python, edit the file
  1. Не закрывая Python, откройте simplemod.py в WebIDE и измените его: Не закрывая интерпретатор Python, откройте файл simplemod.py в WebIDE и внесите еще одно изменение в метод yow() класса Spam. Это поможет нам увидеть, как поведение существующих и новых экземпляров меняется после перезагрузки модуля.
## simplemod.py
## ... (оставьте остальную часть файла без изменений)

class Spam:
    def yow(self):
        print('Super Yow!')  ## Changed one more time
  1. Сохраните файл и вернитесь в интерпретатор Python: Сохраните изменения в файле simplemod.py и вернитесь в интерпретатор Python. Перезагрузите модуль simplemod с помощью функции importlib.reload(). Затем протестируйте как старый, так и новый экземпляры класса Spam, чтобы увидеть, как они ведут себя.
>>> import importlib
>>> importlib.reload(simplemod)
Loaded simplemod
<module 'simplemod' from 'simplemod.py'>

>>> ## Try the old instance
>>> s.yow()
Even More Yow!  ## Still uses the old implementation

>>> ## Create a new instance
>>> t = simplemod.Spam()
>>> t.yow()
Super Yow!  ## Uses the new implementation

Обратите внимание, что старый экземпляр s по-прежнему использует старую реализацию, в то время как новый экземпляр t использует новую. Это происходит потому, что перезагрузка модуля не обновляет существующие экземпляры классов. Когда создается экземпляр класса, он хранит ссылку на объект класса в тот момент. Перезагрузка модуля создает новый объект класса, но существующие экземпляры по-прежнему ссылаются на старый объект класса.

  1. Вы также можете наблюдать другие необычные поведения: Мы можем дополнительно наблюдать ограничения перезагрузки модулей, используя функцию isinstance(). Эта функция проверяет, является ли объект экземпляром определенного класса. После перезагрузки модуля мы увидим, что старый экземпляр s больше не считается экземпляром нового класса simplemod.Spam, в то время как новый экземпляр t считается.
>>> isinstance(s, simplemod.Spam)
False
>>> isinstance(t, simplemod.Spam)
True

Это показывает, что после перезагрузки simplemod.Spam ссылается на другой объект класса, чем тот, который был использован для создания s.

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

Резюме

В этом практическом занятии вы изучили основы работы с модулями в Python. Вы узнали, как создать модуль Python в виде файла .py с переменными, функциями и классами, а также как импортировать модули с использованием оператора import. Вы также понимаете, что Python загружает модули только один раз за сессию интерпретатора и что можно использовать функцию importlib.reload() для принудительной перезагрузки.

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