Menu
CoddyTech

Nạp chồng toán tử

Thuộc phần Lập trình hướng đối tượng trong hành trình Python của Coddy. Bài học 34 trên 64.

Nạp chồng toán tử cho phép các lớp của bạn hoạt động với các toán tử tích hợp sẵn của Python (+, -, *, v.v.) bằng cách triển khai các phương thức ma thuật đặc biệt.

Đây là ví dụ về một lớp có nạp chồng toán tử:

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})"

Phương thức __add__ xác định điều gì xảy ra khi bạn dùng toán tử +:

v1 = Vector(2, 3)
v2 = Vector(5, 7)
result = v1 + v2  # Gọi v1.__add__(v2)
print(result)

Phương thức __mul__ quy định điều gì xảy ra khi bạn dùng toán tử *:

v1 = Vector(2, 3)
scaled = v1 * 3   # Gọi v1.__mul__(3)
print(scaled)

Đầu ra:

Vector(7, 10)
Vector(6, 9)

Thêm các toán tử so sánh:

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: gọi v1.__eq__(v2)
print(v1 == v3)  # False

Điểm chính: Nạp chồng toán tử (operator overloading) dùng các phương thức đặc biệt như __add__ (+), __sub__ (-), __mul__ (*), __eq__ (==) để xác định cách các toán tử hoạt động với đối tượng của bạn. Nhờ vậy, các lớp hoạt động tự nhiên với những toán tử tích hợp sẵn của Python.

challenge icon

Thử thách

Trung bình

Trong thử thách này, bạn sẽ triển khai lớp Money để biểu diễn các khoản tiền và nạp chồng toán tử một cách toàn diện. Lời giải của bạn sẽ được kiểm tra kỹ lưỡng bằng một bộ test case toàn diện.

  • money.py , đây là tệp duy nhất bạn cần chỉnh sửa. Tệp chứa định nghĩa lớp cùng các chú thích TODO hướng dẫn bạn triển khai.
  • driver.py , tệp này chứa nhiều kịch bản kiểm thử để xác thực lời giải của bạn (không chỉnh sửa).

Triển khai lớp Money với các tính năng sau:

  1. Hàm khởi tạo nhận amount (số thực) và currency (chuỗi)
  2. Cộng (+) các đối tượng Money có cùng đơn vị tiền tệ
  3. Nhân (*) với một số để thay đổi quy mô khoản tiền
  4. So sánh bằng (==) giữa các đối tượng Money
  5. Biểu diễn chuỗi theo định dạng “X.XX CUR”

Tự làm thử

from money import Money

# Xử lý test case
test_case = input()

def test_basic_functionality():
    # Kiểm tra khởi tạo và biểu diễn dưới dạng chuỗi (string)
    m1 = Money(10.0, "USD")
    assert str(m1) == "10.00 USD", f"__str__ method failed, got {str(m1)}"
    
    # Kiểm tra phép cộng
    m2 = Money(20.0, "USD")
    m3 = m1 + m2
    assert str(m3) == "30.00 USD", f"Addition failed, got {str(m3)}"
    
    # Kiểm tra phép cộng giữa các đơn vị tiền tệ khác nhau
    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)}"
    
    # Kiểm tra phép nhân
    m6 = m1 * 3
    assert str(m6) == "30.00 USD", f"Multiplication failed, got {str(m6)}"
    
    # Kiểm tra tính bằng nhau
    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():
    # Kiểm tra với số tiền bằng 0
    m1 = Money(0.0, "USD")
    assert str(m1) == "0.00 USD", f"Zero amount string representation failed, got {str(m1)}"
    
    # Kiểm tra phép cộng với 0
    m2 = Money(10.0, "USD")
    m3 = m1 + m2
    assert str(m3) == "10.00 USD", f"Addition with zero failed, got {str(m3)}"
    
    # Kiểm tra phép nhân với 0
    m4 = m2 * 0
    assert str(m4) == "0.00 USD", f"Multiplication by zero failed, got {str(m4)}"
    
    # Kiểm tra tính bằng nhau khi số tiền bằng 0
    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():
    # Kiểm tra với số tiền âm
    m1 = Money(-10.0, "USD")
    assert str(m1) == "-10.00 USD", f"Negative amount string representation failed, got {str(m1)}"
    
    # Kiểm tra phép cộng với số tiền âm
    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)}"
    
    # Kiểm tra phép nhân với một số âm
    m6 = m2 * -2
    assert str(m6) == "-40.00 USD", f"Multiplication by negative scalar failed, got {str(m6)}"
    
    # Kiểm tra tính bằng nhau của các số tiền âm
    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():
    # Kiểm tra với các số tiền rất lớn
    m1 = Money(1000000.0, "USD")
    assert str(m1) == "1000000.00 USD", f"Large amount string representation failed, got {str(m1)}"
    
    # Kiểm tra phép cộng các số tiền lớn
    m2 = Money(2000000.0, "USD")
    m3 = m1 + m2
    assert str(m3) == "3000000.00 USD", f"Addition with large amounts failed, got {str(m3)}"
    
    # Kiểm tra phép nhân với một số lớn
    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():
    # Kiểm tra với các số tiền có phần thập phân
    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)}"
    
    # Kiểm tra phép cộng các số tiền có phần thập phân
    m3 = Money(0.75, "USD")
    m4 = m1 + m3
    assert str(m4) == "11.00 USD", f"Addition with fractional amounts failed, got {str(m4)}"
    
    # Kiểm tra phép nhân với một số có phần thập phân
    m5 = m1 * 0.4
    assert str(m5) == "4.10 USD", f"Multiplication with fractional scalar failed, got {str(m5)}"
    
    print("All precision tests passed!")

def test_type_validation():
    try:
        # Các phép toán này không được gây ra lỗi
        m1 = Money(10.0, "USD")
        m2 = m1 * 2
        m3 = m1 * 2.5
        
        # Kiểm tra tính bằng nhau giữa các kiểu dữ liệu khác nhau
        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():
    # Kiểm tra phân biệt chữ hoa và chữ thường trong mã tiền tệ
    m1 = Money(10.0, "USD")
    m2 = Money(10.0, "usd")
    
    # Mã tiền tệ phải phân biệt chữ hoa và chữ thường
    assert m1 != m2, "Currency comparison should be case-sensitive"
    
    # Cộng các đơn vị tiền tệ khác nhau về chữ hoa, chữ thường phải báo lỗi
    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():
    # Tạo nhiều đối tượng Money và thực hiện các phép tính
    base = Money(1.0, "USD")
    result = base
    
    # Thực hiện 1000 phép cộng
    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)}"
    
    # Thực hiện 10 phép nhân
    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!")

# Chạy bài kiểm tra phù hợp dựa trên đầu vào
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 iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Python online