Модуль в Python - это файл, содержащий код Python (переменные, функции, классы и т.д.), который можно импортировать и использовать в других программах. Модули позволяют организовать код, повторно использовать его и создавать библиотеки функциональности.
Создание модуля в Python очень просто - достаточно создать файл с расширением .py и поместить в него нужный код:
# math_utils.py - наш собственный модуль
"""
Модуль с математическими утилитами
Содержит полезные функции для математических операций
"""
# Константы
ПИ = 3.141592653589793
Е = 2.718281828459045
# Функции
def факториал(n):
"""Вычисляет факториал числа n"""
if n < 0:
raise ValueError("Факториал определен только для неотрицательных чисел")
if n == 0 or n == 1:
return 1
результат = 1
for i in range(2, n + 1):
результат *= i
return результат
def наибольший_общий_делитель(a, b):
"""Вычисляет наибольший общий делитель двух чисел"""
while b:
a, b = b, a % b
return a
def наименьшее_общее_кратное(a, b):
"""Вычисляет наименьшее общее кратное двух чисел"""
return abs(a * b) // наибольший_общий_делитель(a, b)
def простое_число(n):
"""Проверяет, является ли число простым"""
if n < 2:
return False
for i in range(2, int(n ** 0.5) + 1):
if n % i == 0:
return False
return True
def список_простых(предел):
"""Возвращает список простых чисел до заданного предела"""
простые = []
for число in range(2, предел + 1):
if простое_число(число):
простые.append(число)
return простые
# Классы
class КомплексноеЧисло:
def __init__(self, действительная, мнимая=0):
self.действительная = действительная
self.мнимая = мнимая
def __str__(self):
if self.мнимая >= 0:
return f"{self.действительная} + {self.мнимая}i"
else:
return f"{self.действительная} - {abs(self.мнимая)}i"
def __add__(self, другое):
if isinstance(другое, КомплексноеЧисло):
return КомплексноеЧисло(
self.действительная + другое.действительная,
self.мнимая + другое.мнимая
)
elif isinstance(другое, (int, float)):
return КомплексноеЧисло(
self.действительная + другое,
self.мнимая
)
else:
return NotImplemented
def модуль(self):
return (self.действительная ** 2 + self.мнимая ** 2) ** 0.5
# Защита от прямого выполнения
if "__main__" == "__main__": # Это будет False при импорте
print("Этот код выполняется только при прямом запуске модуля")
print(f"Факториал 5: {факториал(5)}")
print(f"Простые числа до 20: {список_простых(20)}")
Существует несколько способов импорта модулей в Python:
# main.py - файл, использующий наш модуль
# 1. Полный импорт модуля
import math_utils
print(f"Значение ПИ: {math_utils.ПИ}")
print(f"Факториал 6: {math_utils.факториал(6)}")
print(f"НОД(48, 18): {math_utils.наибольший_общий_делитель(48, 18)}")
# 2. Импорт с псевдонимом
import math_utils as mu
print(f"НОК(12, 18): {mu.наименьшее_общее_кратное(12, 18)}")
# 3. Импорт конкретных элементов
from math_utils import факториал, простое_число
print(f"Факториал 7: {факториал(7)}")
print(f"17 простое число? {простое_число(17)}")
# 4. Импорт всех элементов (не рекомендуется)
from math_utils import *
print(f"Значение Е: {Е}")
print(f"Простые числа до 10: {список_простых(10)}")
# 5. Импорт класса из модуля
from math_utils import КомплексноеЧисло
число1 = КомплексноеЧисло(3, 4)
число2 = КомплексноеЧисло(1, -2)
print(f"Число 1: {число1}")
print(f"Число 2: {число2}")
print(f"Сумма: {число1 + число2}")
print(f"Модуль числа 1: {число1.модуль():.2f}")
Для создания пакетов (коллекций модулей) используется файл __init__.py:
# Структура пакета geometry/
# geometry/
# ├── __init__.py
# ├── circle.py
# ├── rectangle.py
# └── triangle.py
# geometry/__init__.py
"""
Геометрический пакет
Содержит модули для работы с различными геометрическими фигурами
"""
# Импортируем основные классы для удобства использования
from .circle import Круг
from .rectangle import Прямоугольник
from .triangle import Треугольник
# Определяем, что будет доступно при импорте *
__all__ = ['Круг', 'Прямоугольник', 'Треугольник']
# Определяем версию пакета
__version__ = "1.0.0"
print("Геометрический пакет загружен")
# geometry/circle.py
import math
class Круг:
def __init__(self, радиус):
self.радиус = радиус
def площадь(self):
return math.pi * self.радиус ** 2
def периметр(self):
return 2 * math.pi * self.радиус
def __str__(self):
return f"Круг(радиус={self.радиус})"
# geometry/rectangle.py
class Прямоугольник:
def __init__(self, ширина, высота):
self.ширина = ширина
self.высота = высота
def площадь(self):
return self.ширина * self.высота
def периметр(self):
return 2 * (self.ширина + self.высота)
def __str__(self):
return f"Прямоугольник(ширина={self.ширина}, высота={self.высота})"
# geometry/triangle.py
import math
class Треугольник:
def __init__(self, сторона_a, сторона_b, сторона_c):
if not self._может_существовать(сторона_a, сторона_b, сторона_c):
raise ValueError("Треугольник с такими сторонами не может существовать")
self.сторона_a = сторона_a
self.сторона_b = сторона_b
self.сторона_c = сторона_c
def _может_существовать(self, a, b, c):
return a + b > c and a + c > b and b + c > a
def площадь(self):
# Формула Герона
p = self.периметр() / 2
return math.sqrt(p * (p - self.сторона_a) * (p - self.сторона_b) * (p - self.сторона_c))
def периметр(self):
return self.сторона_a + self.сторона_b + self.сторона_c
def __str__(self):
return f"Треугольник(стороны={self.сторона_a}, {self.сторона_b}, {self.сторона_c})"
# Использование пакета
# main.py
import geometry
круг = geometry.Круг(5)
прямоугольник = geometry.Прямоугольник(4, 6)
треугольник = geometry.Треугольник(3, 4, 5)
print(f"{круг}: площадь = {круг.площадь():.2f}, периметр = {круг.периметр():.2f}")
print(f"{прямоугольник}: площадь = {прямоугольник.площадь()}, периметр = {прямоугольник.периметр()}")
print(f"{треугольник}: площадь = {треугольник.площадь():.2f}, периметр = {треугольник.периметр()}")
В пакетах можно использовать относительные импорты для ссылки на другие модули в том же пакете:
# Структура пакета mypackage/
# mypackage/
# ├── __init__.py
# ├── core.py
# ├── utils.py
# └── subpackage/
# ├── __init__.py
# └── helpers.py
# mypackage/core.py
class ОсновнойКласс:
def __init__(self, имя):
self.имя = имя
def приветствие(self):
return f"Привет, я {self.имя}"
# mypackage/utils.py
from .core import ОсновнойКласс # Относительный импорт
def создать_объект(имя):
return ОсновнойКласс(имя)
def обработать_строку(строка):
return строка.strip().upper()
# mypackage/subpackage/helpers.py
from ..core import ОсновнойКласс # Относительный импорт из родительского пакета
from ..utils import обработать_строку # Относительный импорт из соседнего модуля
def помощник(имя):
обработанное_имя = обработать_строку(имя)
объект = ОсновнойКласс(обработанное_имя)
return f"Помощник создал: {объект.приветствие()}"
# mypackage/__init__.py
from .core import ОсновнойКласс
from .utils import создать_объект, обработать_строку
from .subpackage.helpers import помощник
__all__ = ['ОсновнойКласс', 'создать_объект', 'обработать_строку', 'помощник']
# main.py - использование пакета
import mypackage
# Абсолютные импорты
объект1 = mypackage.ОсновнойКласс("Абсолют")
print(объект1.приветствие())
объект2 = mypackage.создать_объект("Функция")
print(объект2.приветствие())
print(mypackage.обработать_строку(" привет, мир "))
print(mypackage.помощник("относительный импорт"))
Рассмотрим практические примеры создания и использования модулей:
# Структура пакета library/
# library/
# ├── __init__.py
# ├── book.py
# ├── member.py
# └── library.py
# library/book.py
class Книга:
def __init__(self, isbn, название, автор, год_издания):
self.isbn = isbn
self.название = название
self.автор = автор
self.год_издания = год_издания
self.доступна = True
self.владелец = None
def выдать(self, читатель):
if not self.доступна:
raise ValueError(f"Книга '{self.название}' уже выдана")
self.доступна = False
self.владелец = читатель
return True
def вернуть(self):
if self.доступна:
raise ValueError(f"Книга '{self.название}' уже в библиотеке")
self.доступна = True
предыдущий_владелец = self.владелец
self.владелец = None
return предыдущий_владелец
def __str__(self):
статус = "доступна" if self.доступна else f"выдана {self.владелец.имя}"
return f"'{self.название}' by {self.автор} ({self.год_издания}) - {статус}"
# library/member.py
class Читатель:
def __init__(self, id_читателя, имя, email):
self.id_читателя = id_читателя
self.имя = имя
self.email = email
self.книги = []
def взять_книгу(self, книга):
if len(self.книги) >= 5:
raise ValueError(f"{self.имя} уже имеет максимальное количество книг")
if книга in self.книги:
raise ValueError(f"{self.имя} уже имеет эту книгу")
self.книги.append(книга)
def вернуть_книгу(self, книга):
if книга not in self.книги:
raise ValueError(f"{self.имя} не имеет этой книги")
self.книги.remove(книга)
def список_книг(self):
return [str(книга) for книга in self.книги]
def __str__(self):
return f"Читатель {self.имя} (ID: {self.id_читателя})"
# library/library.py
from .book import Книга
from .member import Читатель
class Библиотека:
def __init__(self, название):
self.название = название
self.книги = {}
self.читатели = {}
def добавить_книгу(self, книга):
if книга.isbn in self.книги:
raise ValueError(f"Книга с ISBN {книга.isbn} уже существует")
self.книги[книга.isbn] = книга
print(f"Книга '{книга.название}' добавлена в библиотеку")
def зарегистрировать_читателя(self, читатель):
if читатель.id_читателя in self.читатели:
raise ValueError(f"Читатель с ID {читатель.id_читателя} уже зарегистрирован")
self.читатели[читатель.id_читателя] = читатель
print(f"Читатель '{читатель.имя}' зарегистрирован")
def найти_книгу(self, isbn_или_название):
# Поиск по ISBN
if isbn_или_название in self.книги:
return self.книги[isbn_или_название]
# Поиск по названию
for книга in self.книги.values():
if isbn_или_название.lower() in книга.название.lower():
return книга
return None
def выдать_книгу(self, isbn_книги, id_читателя):
if isbn_книги not in self.книги:
raise ValueError(f"Книга с ISBN {isbn_книги} не найдена")
if id_читателя not in self.читатели:
raise ValueError(f"Читатель с ID {id_читателя} не найден")
книга = self.книги[isbn_книги]
читатель = self.читатели[id_читателя]
try:
книга.выдать(читатель)
читатель.взять_книгу(книга)
print(f"Книга '{книга.название}' выдана читателю '{читатель.имя}'")
except ValueError as e:
print(f"Ошибка: {e}")
def вернуть_книгу(self, isbn_книги):
if isbn_книги not in self.книги:
raise ValueError(f"Книга с ISBN {isbn_книги} не найдена")
книга = self.книги[isbn_книги]
try:
читатель = книга.вернуть()
читатель.вернуть_книгу(книга)
print(f"Книга '{книга.название}' возвращена читателем '{читатель.имя}'")
except ValueError as e:
print(f"Ошибка: {e}")
def показать_все_книги(self):
if not self.книги:
print("Библиотека пуста")
return
print(f"\nКниги в библиотеке '{self.название}':")
for книга in self.книги.values():
print(f" {книга}")
# library/__init__.py
from .book import Книга
from .member import Читатель
from .library import Библиотека
__all__ = ['Книга', 'Читатель', 'Библиотека']
# main.py - использование системы библиотеки
import library
# Создание библиотеки
библиотека = library.Библиотека("Городская библиотека")
# Создание книг
книга1 = library.Книга("978-0134685991", "Effective Java", "Joshua Bloch", 2017)
книга2 = library.Книга("978-1449355739", "Learning Python", "Mark Lutz", 2013)
книга3 = library.Книга("978-0596158064", "Python Cookbook", "David Beazley", 2013)
# Добавление книг в библиотеку
библиотека.добавить_книгу(книга1)
библиотека.добавить_книгу(книга2)
библиотека.добавить_книгу(книга3)
# Создание читателей
читатель1 = library.Читатель(1, "Анна", "anna@email.com")
читатель2 = library.Читатель(2, "Борис", "boris@email.com")
# Регистрация читателей
библиотека.зарегистрировать_читателя(читатель1)
библиотека.зарегистрировать_читателя(читатель2)
# Показать все книги
библиотека.показать_все_книги()
# Выдача книг
библиотека.выдать_книгу("978-0134685991", 1)
библиотека.выдать_книгу("978-1449355739", 2)
# Показать книги после выдачи
библиотека.показать_все_книги()
# Попытка выдать уже выданную книгу
библиотека.выдать_книгу("978-0134685991", 2)
# Возврат книги
библиотека.вернуть_книгу("978-0134685991")
# Показать книги после возврата
библиотека.показать_все_книги()
Попробуйте решить следующие задачи, применяя полученные знания о создании модулей:
Создайте модуль string_utils.py, который будет содержать полезные функции для работы со строками: подсчет гласных, проверка палиндрома, удаление дубликатов слов, шифрование Цезаря. Создайте документацию к модулю и протестируйте его в отдельном файле.
# string_utils.py
"""
Модуль для работы со строками
Содержит полезные функции для манипуляций со строками
"""
def подсчет_гласных(строка):
"""Подсчитывает количество гласных в строке"""
гласные = "аеёиоуыэюяaeiou"
return sum(1 for символ in строка.lower() if символ in гласные)
def палиндром(строка):
"""Проверяет, является ли строка палиндромом"""
очищенная = "".join(символ.lower() for символ in строка if символ.isalnum())
return очищенная == очищенная[::-1]
def удалить_дубликаты_слов(строка):
"""Удаляет дубликаты слов из строки, сохраняя порядок"""
слова = строка.split()
уникальные_слова = []
for слово in слова:
if слово not in уникальные_слова:
уникальные_слова.append(слово)
return " ".join(уникальные_слова)
def шифр_цезаря(строка, сдвиг=3):
"""Шифрует строку с помощью шифра Цезаря"""
результат = ""
for символ in строка:
if символ.isalpha():
# Определяем базу (A для заглавных, a для строчных)
база = ord('A') if символ.isupper() else ord('a')
# Применяем сдвиг с учетом цикличности
сдвинутый = (база + (ord(символ) - база + сдвиг) % 26)
результат += chr(сдвинутый)
else:
результат += символ
return результат
# test_string_utils.py
import string_utils
print(f"Гласных в 'Привет, мир!': {string_utils.подсчет_гласных('Привет, мир!')}")
print(f"'А роза упала на лапу Азора' - палиндром? {string_utils.палиндром('А роза упала на лапу Азора')}")
print(f"Без дубликатов: {string_utils.удалить_дубликаты_слов('это это тест тест строка')}")
print(f"Шифр Цезаря: {string_utils.шифр_цезаря('Привет, Мир!', 3)}")
Создайте пакет date_utils с модулями для работы с датами: форматирование дат, вычисление возраста, определение високосного года, расчет дней до события. Организуйте пакет с использованием __init__.py и относительных импортов.
# Структура пакета date_utils/
# date_utils/
# ├── __init__.py
# ├── formatting.py
# ├── calculations.py
# └── helpers.py
# date_utils/formatting.py
from datetime import datetime
def форматировать_дату(дата, формат="%d.%m.%Y"):
"""Форматирует дату в заданный формат"""
if isinstance(дата, str):
дата = datetime.strptime(дата, "%Y-%m-%d")
return дата.strftime(формат)
def текущая_дата(формат="%d.%m.%Y"):
"""Возвращает текущую дату в заданном формате"""
return datetime.now().strftime(формат)
# date_utils/calculations.py
from datetime import datetime, date
def вычислить_возраст(дата_рождения):
"""Вычисляет возраст по дате рождения"""
if isinstance(дата_рождения, str):
дата_рождения = datetime.strptime(дата_рождения, "%Y-%m-%d").date()
сегодня = date.today()
возраст = сегодня.year - дата_рождения.year
# Проверяем, был ли день рождения в этом году
if (сегодня.month, сегодня.day) < (дата_рождения.month, дата_рождения.day):
возраст -= 1
return возраст
def високосный_год(год=None):
"""Проверяет, является ли год високосным"""
if год is None:
год = datetime.now().year
return год % 4 == 0 and (год % 100 != 0 or год % 400 == 0)
# date_utils/helpers.py
from datetime import datetime, timedelta
def дней_до_события(дата_события):
"""Вычисляет количество дней до события"""
if isinstance(дата_события, str):
дата_события = datetime.strptime(дата_события, "%Y-%m-%d").date()
сегодня = datetime.now().date()
разница = дата_события - сегодня
return разница.days
def добавить_дни(дата, дни):
"""Добавляет заданное количество дней к дате"""
if isinstance(дата, str):
дата = datetime.strptime(дата, "%Y-%m-%d").date()
новая_дата = дата + timedelta(days=дни)
return новая_дата.strftime("%Y-%m-%d")
# date_utils/__init__.py
from .formatting import форматировать_дату, текущая_дата
from .calculations import вычислить_возраст, високосный_год
from .helpers import дней_до_события, добавить_дни
__all__ = [
'форматировать_дату', 'текущая_дата',
'вычислить_возраст', 'високосный_год',
'дней_до_события', 'добавить_дни'
]
# test_date_utils.py
import date_utils
print(f"Текущая дата: {date_utils.текущая_дата()}")
print(f"Форматированная дата: {date_utils.форматировать_дату('2023-12-25', '%d %B %Y')}")
print(f"Возраст (дата рождения 1990-05-15): {date_utils.вычислить_возраст('1990-05-15')} лет")
print(f"2024 год високосный? {date_utils.високосный_год(2024)}")
print(f"Дней до Нового года: {date_utils.дней_до_события('2024-01-01')}")
print(f"Через 30 дней будет: {date_utils.добавить_дни('2023-10-01', 30)}")
Создайте модуль config_manager.py, который позволяет загружать, сохранять и обновлять конфигурационные файлы в формате JSON. Модуль должен обрабатывать исключения при работе с файлами и предоставлять удобный интерфейс для доступа к параметрам конфигурации.
# config_manager.py
import json
import os
from typing import Any, Dict, Optional
class ConfigManager:
def __init__(self, файл_конфигурации: str):
self.файл_конфигурации = файл_конфигурации
self.конфигурация: Dict[str, Any] = {}
self.загрузить()
def загрузить(self) -> bool:
"""Загружает конфигурацию из файла"""
try:
if os.path.exists(self.файл_конфигурации):
with open(self.файл_конфигурации, 'r', encoding='utf-8') as файл:
self.конфигурация = json.load(файл)
return True
else:
print(f"Файл конфигурации {self.файл_конфигурации} не найден. Создается пустая конфигурация.")
return False
except json.JSONDecodeError as e:
print(f"Ошибка чтения JSON: {e}")
return False
except Exception as e:
print(f"Ошибка загрузки конфигурации: {e}")
return False
def сохранить(self) -> bool:
"""Сохраняет конфигурацию в файл"""
try:
with open(self.файл_конфигурации, 'w', encoding='utf-8') as файл:
json.dump(self.конфигурация, файл, ensure_ascii=False, indent=4)
return True
except Exception as e:
print(f"Ошибка сохранения конфигурации: {e}")
return False
def получить(self, ключ: str, значение_по_умолчанию: Any = None) -> Any:
"""Получает значение параметра по ключу"""
return self.конфигурация.get(ключ, значение_по_умолчанию)
def установить(self, ключ: str, значение: Any) -> None:
"""Устанавливает значение параметра"""
self.конфигурация[ключ] = значение
def обновить(self, новые_параметры: Dict[str, Any]) -> None:
"""Обновляет несколько параметров"""
self.конфигурация.update(новые_параметры)
def удалить(self, ключ: str) -> bool:
"""Удаляет параметр по ключу"""
if ключ in self.конфигурация:
del self.конфигурация[ключ]
return True
return False
def все_параметры(self) -> Dict[str, Any]:
"""Возвращает все параметры конфигурации"""
return self.конфигурация.copy()
# test_config_manager.py
from config_manager import ConfigManager
# Создание менеджера конфигурации
config = ConfigManager("app_config.json")
# Установка параметров
config.установить("database_url", "localhost:5432")
config.установить("debug", True)
config.установить("max_connections", 100)
# Обновление нескольких параметров
config.обновить({
"timeout": 30,
"retry_attempts": 3,
"log_level": "INFO"
})
# Сохранение конфигурации
if config.сохранить():
print("Конфигурация сохранена")
# Загрузка конфигурации
config2 = ConfigManager("app_config.json")
print(f"Database URL: {config2.получить('database_url')}")
print(f"Debug mode: {config2.получить('debug')}")
print(f"Все параметры: {config2.все_параметры()}")