Menu
CoddyTech

Thành viên public, protected, private

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 28 trên 64.

Python có ba mức kiểm soát quyền truy cập đối với các thành viên của lớp: công khai, được bảo vệ và riêng tư. Các mức này kiểm soát cách truy cập thuộc tính và phương thức.

Đây là ví dụ về một lớp có cả ba mức truy cập:

class BankAccount:
    def __init__(self, owner, balance, account_id):
        self.owner = owner           # Công khai: có thể truy cập từ mọi nơi
        self._balance = balance      # Được bảo vệ: dùng trong nội bộ
        self.__account_id = account_id  # Riêng tư: chỉ dùng trong lớp
    
    def deposit(self, amount):       # Phương thức (method) công khai
        self._balance += amount
    
    def _calculate_interest(self):   # Phương thức được bảo vệ
        return self._balance * 0.02
    
    def __validate_transaction(self, amount):  # Phương thức riêng tư
        return amount > 0 and amount <= self._balance

Truy cập các thành viên công khai từ bất kỳ đâu:

account = BankAccount("Alice", 1000, "12345")
print(account.owner)        # Alice
account.deposit(500)        # Hoạt động bình thường

Truy cập các thành viên được bảo vệ (một dấu gạch dưới, chỉ là quy ước):

print(account._balance)     # 1500, chạy được nhưng không nên dùng
result = account._calculate_interest()  # Chạy được nhưng không nên dùng
print(result)               # 30.0

Thử truy cập các thành viên riêng tư (hai dấu gạch dưới, biến đổi tên):

# Cách này không chạy:
# print(account.__account_id)  # AttributeError

# Nhưng cách này chạy được (biến đổi tên):
print(account._BankAccount__account_id)  # 12345

Tạo một lớp con để minh họa sự khác nhau giữa quyền truy cập protected và private:

class SavingsAccount(BankAccount):
    def show_balance(self):
        return self._balance        # Được bảo vệ, có thể truy cập trong lớp con
    
    def show_id(self):
        # return self.__account_id  # Cách này không hoạt động vì đây là thuộc tính riêng tư
        return "Cannot access private member"
savings = SavingsAccount("Bob", 2000, "67890")
print(savings.show_balance())  # 2000
print(savings.show_id())       # Không thể truy cập thành viên riêng tư

Đầu ra:

Alice
30.0
12345
2000
Cannot access private member

Điểm chính: Các thành viên công khai không có tiền tố và có thể được truy cập ở bất kỳ đâu. Các thành viên được bảo vệ dùng một dấu gạch dưới (_) và chỉ nên được dùng trong hệ thống phân cấp lớp. Các thành viên riêng tư dùng hai dấu gạch dưới (__), tên của chúng được biến đổi để tăng cường tính riêng tư. Quyền truy cập trong Python dựa trên quy ước, không được thực thi nghiêm ngặt.

challenge icon

Thử thách

Trung bình

Trong thử thách này, bạn sẽ triển khai lớp BankAccount để minh họa cách sử dụng đúng các mức truy cập công khai, được bảo vệ và riêng tư trong Python.

  • bankaccount.py - Chứa định nghĩa lớp với các chú thích TODO hướng dẫn bạn triển khai
  1. Làm theo các chú thích TODO trong bankaccount.py để triển khai chức năng cần thiết
  2. Triển khai đúng các mức truy cập (công khai, được bảo vệ, riêng tư) theo yêu cầu
  3. Đảm bảo mọi phương thức xử lý các trường hợp ngoại lệ phù hợp (tiền gửi âm, v.v.)

Tự làm thử

from bank_account import BankAccount

# Xử lý toàn diện các test case
test_case = input()

if test_case == "basic_test":
    # Kiểm tra chức năng cơ bản
    account = BankAccount("John Doe", "12345")
    print(account.account_holder)  # Thuộc tính (attribute) công khai
    print(account.deposit(100))    # Phương thức (method) công khai
    print(account.get_balance())   # Phương thức (method) công khai

elif test_case == "transaction_count":
    # Kiểm tra thuộc tính được bảo vệ
    account = BankAccount("Jane Smith", "67890")
    account.deposit(50)
    account.deposit(100)
    account.deposit(200)
    print(account._transaction_count)  # Thuộc tính được bảo vệ
    print(account.get_balance())

elif test_case == "invalid_deposit":
    # Kiểm tra việc xác thực trong phương thức deposit
    account = BankAccount("Bob Johnson", "54321")
    print(account.deposit(-50))  # Trả về None
    print(account.deposit(0))    # Trả về None
    print(account.deposit(75))   # Trả về 75
    print(account.get_balance())

elif test_case == "private_access":
    # Kiểm tra quyền truy cập thuộc tính riêng tư
    account = BankAccount("Alice Brown", "98765")
    try:
        print(account.__balance)  # Thao tác này sẽ gây ra AttributeError
    except AttributeError:
        pass
    
    try:
        print(account.__account_number)  # Thao tác này sẽ gây ra AttributeError
    except AttributeError:
        pass
    
    print("Private attributes are not directly accessible")
    account.deposit(500)
    print(account.get_balance())  # Truy cập qua phương thức công khai

elif test_case == "name_mangling":
    # Minh họa cách biến đổi tên của các thuộc tính (attributes) riêng tư
    account = BankAccount("Charlie Green", "13579")
    print(account._BankAccount__balance)        # Truy cập thuộc tính riêng tư qua tên đã biến đổi
    print(account._BankAccount__account_number)  # Truy cập thuộc tính riêng tư qua tên đã biến đổi
    account.deposit(300)
    print(account._BankAccount__balance)  # Sẽ hiển thị số dư đã cập nhật

elif test_case == "multiple_accounts":
    # Kiểm tra nhiều tài khoản
    account1 = BankAccount("David White", "24680")
    account2 = BankAccount("Eva Black", "86420")
    account3 = BankAccount("Frank Red", "97531")
    
    account1.deposit(150)
    account2.deposit(250)
    account2.deposit(50)
    account3.deposit(500)
    account3.deposit(300)
    account3.deposit(200)
    
    print(f"{account1.account_holder}: {account1.get_balance()}")
    print(f"{account2.account_holder}: {account2.get_balance()}")
    print(f"{account3.account_holder}: {account3.get_balance()}")
    
    print(f"Account 1 transactions: {account1._transaction_count}")
    print(f"Account 2 transactions: {account2._transaction_count}")
    print(f"Account 3 transactions: {account3._transaction_count}")

elif test_case == "stress_test":
    # Kiểm tra hiệu năng với nhiều giao dịch
    account = BankAccount("Stress Tester", "11111")
    for _ in range(1000):
        account.deposit(1)
    print(f"Final balance: {account.get_balance()}")
    print(f"Transaction count: {account._transaction_count}")
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