Перегрузка операторов
Часть раздела Object Oriented Programming путешествия по Python на Coddy — урок 34 из 64.
Перегрузка операторов позволяет вашим классам работать со встроенными операторами Python (+, -, *, и т. д.) путем реализации специальных магических методов.
Вот пример класса с перегрузкой операторов:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __str__(self):
return f"Vector({self.x}, {self.y})"Метод __add__ определяет, что происходит при использовании оператора +:
v1 = Vector(2, 3)
v2 = Vector(5, 7)
result = v1 + v2 # Вызывает v1.__add__(v2)
print(result)Метод __mul__ определяет, что происходит при использовании оператора *:
v1 = Vector(2, 3)
scaled = v1 * 3 # Вызывает v1.__mul__(3)
print(scaled)Вывод:
Vector(7, 10)
Vector(6, 9)Добавьте операторы сравнения:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __str__(self):
return f"Vector({self.x}, {self.y})"
v1 = Vector(2, 3)
v2 = Vector(2, 3)
v3 = Vector(1, 1)
print(v1 == v2) # True - вызывает v1.__eq__(v2)
print(v1 == v3) # FalseКлючевой момент: Перегрузка операторов использует магические методы, такие как __add__ (+), __sub__ (-), __mul__ (*), __eq__ (==), чтобы определить, как операторы работают с вашими объектами. Это позволяет вашим классам вести себя естественно со встроенными операторами Python.
Задание
СреднеВ этом испытании вам предстоит реализовать класс Money, который представляет денежные суммы с использованием надежной перегрузки операторов. Ваша реализация будет тщательно проверена с помощью комплексного набора тестов.
money.py— это единственный файл, который вам нужно отредактировать. Он содержит определение класса с комментариями TODO, направляющими вашу реализацию.driver.py— содержит обширные тестовые сценарии, которые проверяют вашу реализацию (не изменять).
Реализуйте класс Money со следующими возможностями:
- Конструктор, который принимает
amount(float) иcurrency(string) - Сложение (
+) объектов Money с одинаковой валютой - Умножение (
*) на число для масштабирования суммы - Сравнение на равенство (
==) между объектами Money - Строковое представление в формате “X.XX CUR”
Шпаргалка
Перегрузка операторов позволяет классам работать со встроенными операторами Python путем реализации специальных магических методов:
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)
def __eq__(self, other):
return self.x == other.x and self.y == other.y
def __str__(self):
return f"Vector({self.x}, {self.y})"Общие магические методы для перегрузки операторов:
__add__для оператора+__sub__для оператора-__mul__для оператора*__eq__для оператора==__str__для строкового представления
Пример использования:
v1 = Vector(2, 3)
v2 = Vector(5, 7)
result = v1 + v2 # Вызывает v1.__add__(v2)
scaled = v1 * 3 # Вызывает v1.__mul__(3)
print(v1 == v2) # Вызывает v1.__eq__(v2)Попробуйте сами
from money import Money
# Обработчик тестовых случаев
test_case = input()
def test_basic_functionality():
# Тест инициализации и строкового представления
m1 = Money(10.0, "USD")
assert str(m1) == "10.00 USD", f"__str__ method failed, got {str(m1)}"
# Тест сложения
m2 = Money(20.0, "USD")
m3 = m1 + m2
assert str(m3) == "30.00 USD", f"Addition failed, got {str(m3)}"
# Тест сложения разных валют
m4 = Money(20.0, "EUR")
try:
m5 = m1 + m4
assert False, "Adding different currencies should raise an error"
except ValueError as e:
assert str(e) == "Cannot add different currencies", f"Wrong error message: {str(e)}"
# Тест умножения
m6 = m1 * 3
assert str(m6) == "30.00 USD", f"Multiplication failed, got {str(m6)}"
# Тест на равенство
assert m1 == Money(10.0, "USD"), "Equality test failed"
assert m1 != m2, "Inequality test failed"
print("All basic functionality tests passed!")
def test_zero_values():
# Тест с нулевой суммой
m1 = Money(0.0, "USD")
assert str(m1) == "0.00 USD", f"Zero amount string representation failed, got {str(m1)}"
# Тест сложения с нулем
m2 = Money(10.0, "USD")
m3 = m1 + m2
assert str(m3) == "10.00 USD", f"Addition with zero failed, got {str(m3)}"
# Тест умножения на ноль
m4 = m2 * 0
assert str(m4) == "0.00 USD", f"Multiplication by zero failed, got {str(m4)}"
# Тест на равенство с нулевой суммой
assert m1 == Money(0.0, "USD"), "Equality with zero amount failed"
assert m1 != m2, "Inequality with zero amount failed"
print("All zero value tests passed!")
def test_negative_values():
# Тест с отрицательной суммой
m1 = Money(-10.0, "USD")
assert str(m1) == "-10.00 USD", f"Negative amount string representation failed, got {str(m1)}"
# Тест сложения с отрицательными суммами
m2 = Money(20.0, "USD")
m3 = m1 + m2
assert str(m3) == "10.00 USD", f"Addition with negative amount failed, got {str(m3)}"
m4 = Money(-5.0, "USD")
m5 = m1 + m4
assert str(m5) == "-15.00 USD", f"Addition of two negative amounts failed, got {str(m5)}"
# Тест умножения на отрицательный скаляр
m6 = m2 * -2
assert str(m6) == "-40.00 USD", f"Multiplication by negative scalar failed, got {str(m6)}"
# Тест на равенство с отрицательными суммами
assert m1 == Money(-10.0, "USD"), "Equality with negative amount failed"
assert m1 != m2, "Inequality with negative amount failed"
print("All negative value tests passed!")
def test_large_values():
# Тест с очень большими суммами
m1 = Money(1000000.0, "USD")
assert str(m1) == "1000000.00 USD", f"Large amount string representation failed, got {str(m1)}"
# Тест сложения с большими суммами
m2 = Money(2000000.0, "USD")
m3 = m1 + m2
assert str(m3) == "3000000.00 USD", f"Addition with large amounts failed, got {str(m3)}"
# Тест умножения на большой скаляр
m4 = m1 * 1000
assert str(m4) == "1000000000.00 USD", f"Multiplication with large scalar failed, got {str(m4)}"
print("All large value tests passed!")
def test_precision():
# Тест с дробными суммами
m1 = Money(10.25, "USD")
assert str(m1) == "10.25 USD", f"Fractional amount string representation failed, got {str(m1)}"
m2 = Money(10.2, "USD")
assert str(m2) == "10.20 USD", f"Two decimal place formatting failed, got {str(m2)}"
# Тест сложения с дробными суммами
m3 = Money(0.75, "USD")
m4 = m1 + m3
assert str(m4) == "11.00 USD", f"Addition with fractional amounts failed, got {str(m4)}"
# Тест умножения на дробный скаляр
m5 = m1 * 0.5
assert str(m5) == "5.13 USD", f"Multiplication with fractional scalar failed, got {str(m5)}"
print("All precision tests passed!")
def test_type_validation():
try:
# Эти операции должны работать без ошибок
m1 = Money(10.0, "USD")
m2 = m1 * 2
m3 = m1 * 2.5
# Тест на равенство с разными типами
assert (m1 == "10.00 USD") == False, "Equality with string should return False"
assert (m1 == 10.0) == False, "Equality with number should return False"
print("All type validation tests passed!")
except Exception as e:
print(f"Type validation test failed: {e}")
def test_currency_case_sensitivity():
# Тест чувствительности валюты к регистру
m1 = Money(10.0, "USD")
m2 = Money(10.0, "usd")
# Валюты должны быть чувствительны к регистру
assert m1 != m2, "Currency comparison should be case-sensitive"
# Сложение разных регистров должно завершиться ошибкой
try:
m3 = m1 + m2
assert False, "Adding different currency cases should raise an error"
except ValueError as e:
assert str(e) == "Cannot add different currencies", f"Wrong error message: {str(e)}"
print("All currency case sensitivity tests passed!")
def test_performance():
# Создание множества объектов Money и выполнение операций
base = Money(1.0, "USD")
result = base
# Выполнение 1000 сложений
for i in range(1000):
result = result + Money(1.0, "USD")
assert str(result) == "1001.00 USD", f"Performance test addition failed, got {str(result)}"
# Выполнение 10 умножений
result = base
for i in range(10):
result = result * 2
assert str(result) == "1024.00 USD", f"Performance test multiplication failed, got {str(result)}"
print("All performance tests passed!")
# Запуск соответствующего теста на основе входных данных
if test_case == "basic_test":
test_basic_functionality()
elif test_case == "zero_values":
test_zero_values()
elif test_case == "negative_values":
test_negative_values()
elif test_case == "large_values":
test_large_values()
elif test_case == "precision":
test_precision()
elif test_case == "type_validation":
test_type_validation()
elif test_case == "currency_case":
test_currency_case_sensitivity()
elif test_case == "performance":
test_performance()
else:
print(f"Unknown test case: {test_case}")В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы ООП
Внешние файлыВведение в ООПКлассы и объектыПараметр selfМетодыАтрибутыМетод-конструктор (__init__)Повторение — Простой калькулятор4Наследование
Базовое наследованиеФункция super()Переопределение методовМножественное наследованиеПорядок разрешения методовИтоги — Иерархия сотрудников7Специальные методы
Введение в магические методыПерегрузка операторовМагические методы контейнеровПовторение — Пользовательский список10Паттерны проектирования. Часть 1
Введение в паттерны проектированияПаттерн SingletonПаттерн FactoryПаттерн ObserverПаттерн Strategy5Полиморфизм
Снова о переопределении методовУтиная типизацияАбстрактные классы и методыПроектирование интерфейсовИтоги — Калькулятор фигур8Продвинутые концепции ООП
Композиция против наследованияМиксиныСтатические методы и методы классаДекораторы классовМенеджеры контекста11Паттерны проектирования. Часть 2
Паттерн КомандаПаттерн АдаптерПаттерн ДекораторПаттерн Шаблонный методПаттерн СостояниеПаттерн Компоновщик3Свойства классов
Переменные экземпляра и классаДекораторы свойствПриватные атрибутыИтоги — Менеджер банковского счета6Инкапсуляция
Public, Protected, Private члены классаМодификаторы доступаСокрытие данныхДекораторы property: продвинутый уровеньИтоги: Система учета студентов12Проект: Управление библиотекой
Обзор проектаКлассы Book и User