Menu
Coddy logo textTech

演算子オーバーロード

CoddyのPythonジャーニー「Object Oriented Programming」セクションの一部 — レッスン 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の組み込み演算子と自然に連携できるようになります。

challenge icon

チャレンジ

中級

このチャレンジでは、堅牢な演算子オーバーロードを備えた、金額を表す Money クラスを実装します。あなたの実装は、包括的なテストスイートに対して徹底的にテストされます。

  • money.py - これは編集が必要な唯一のファイルです。実装のガイドとなる TODO コメントが含まれたクラス定義が含まれています。
  • driver.py - 実装を検証するための広範なテストシナリオが含まれています(変更しないでください)。

以下の機能を備えた Money クラスを実装してください:

  1. amount (float) と currency (string) を受け取るコンストラクタ
  2. 同じ通貨を持つ Money オブジェクト同士の加算 (+)
  3. 金額をスケーリングするための数値による乗算 (*)
  4. Money オブジェクト間の等価比較 (==)
  5. "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}")
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

Object Oriented Programmingのすべてのレッスン