CodeGym /Khóa học Java /Python SELF VI /Các phương thức và trường đặc biệt

Các phương thức và trường đặc biệt

Python SELF VI
Mức độ , Bài học
Có sẵn

5.1 Mức độ truy cập

Có thể bạn đã để ý đến tên kỳ lạ của constructor __init__? Trong tương lai bạn sẽ gặp nó khá thường xuyên đấy.

Trong Python, có nhiều mức độ truy cập khác nhau tới các thuộc tính và phương thức của lớp, giúp kiểm soát khả năng hiển thị và bảo vệ dữ liệu. Các cơ chế quản lý truy cập chính bao gồm việc sử dụng một hoặc hai dấu gạch dưới (___) trước tên thuộc tính hoặc phương thức.

Sử dụng quy ước

Một dấu gạch dưới _ được sử dụng cho các thuộc tính và phương thức không nên được sử dụng bên ngoài lớp hoặc mô-đun. Điều này không bị cấm, nhưng đây là một quy ước mà nên tuân thủ để dễ đọc và bảo trì mã.

Hai dấu gạch dưới __ được sử dụng cho các thuộc tính và phương thức cần thực sự được bảo mật và bảo vệ khỏi truy cập vô tình hoặc có chủ đích từ bên ngoài. Cơ chế name mangling làm cho chúng ít dễ dàng truy cập hơn, nhưng vẫn có thể truy cập thông qua tên đặc biệt.

Các trường và phương thức công khai (public)

Các thuộc tính và phương thức công khai có thể truy cập từ bất kỳ đâu trong mã. Trong Python, theo mặc định tất cả các thuộc tính và phương thức đều công khai, trừ khi tên của chúng bắt đầu bằng dấu gạch dưới.


class MyClass:
    def __init__(self):
        self.public_attribute = "I am public"
        
    def public_method(self):
        return "This is a public method"
        

obj = MyClass()
print(obj.public_attribute)  # Có thể truy cập
print(obj.public_method())  # Có thể truy cập

Qua các mức độ truy cập trong Python, chúng ta thực hiện đóng gói, cụ thể bằng các trường và phương thức không công khai.

5.2 Các trường và phương thức không công khai

Các trường và phương thức bảo vệ (protected)

Các thuộc tính và phương thức bảo vệ được chỉ định bằng một dấu gạch dưới _ trước tên và dành cho sử dụng nội bộ trong lớp và các lớp con của nó. Đây là một quy ước nói với lập trình viên rằng dữ liệu không dành cho sử dụng bên ngoài lớp.


class MyClass:
    def __init__(self):
        self._protected_attribute = "I am protected"
        
    def _protected_method(self):
        return "This is a protected method"
        

obj = MyClass()
print(obj._protected_attribute)  # Có thể truy cập, nhưng không được khuyến nghị
print(obj._protected_method())  # Có thể truy cập, nhưng không được khuyến nghị

Các trường và phương thức riêng (private)

Trong Python, các thuộc tính và phương thức riêng được chỉ định bằng hai dấu gạch dưới __ trước tên. Các thuộc tính và phương thức này dành cho sử dụng độc quyền trong lớp và mục tiêu chính của chúng là che giấu thực thi nội bộ và bảo vệ dữ liệu khỏi sự thay đổi hoặc sử dụng vô tình từ bên ngoài.

Để ngăn chặn truy cập trực tiếp vào các thuộc tính và phương thức này từ mã bên ngoài, Python áp dụng một cơ chế đặc biệt, được gọi là name mangling (sự biến dạng của tên). Cơ chế này tự động thay đổi tên của các thuộc tính riêng, thêm vào chúng một tiền tố gồm tên lớp. Vì vậy, thuộc tính riêng __private_attribute trong lớp MyClass sẽ được chuyển đổi thành tên nội bộ _MyClass__private_attribute.

Điều này cho phép bảo vệ dữ liệu khỏi truy cập không mong muốn, trong khi vẫn giữ khả năng làm việc với chúng trong lớp. Tuy nhiên, quan trọng là bạn cần nhớ rằng cơ chế "name mangling" không phải là sự bảo vệ tuyệt đối — lập trình viên có kinh nghiệm có thể truy cập vào các dữ liệu này bằng cách sử dụng tên đã thay đổi.

Hãy cùng xem cách nó hoạt động trong thực tế:


class MyClass:
    def __init__(self):
        self.__private_attribute = "I am private"
        
    def __private_method(self):
        return "This is a private method"
        
    def access_private_method(self):
        return self.__private_method()
         

obj = MyClass()
# print(obj.__private_attribute)  # Lỗi, không thể truy cập trực tiếp
# print(obj.__private_method())  # Lỗi, không thể truy cập trực tiếp
print(obj.access_private_method())  # Có thể truy cập qua phương thức công khai của lớp

Như bạn thấy từ ví dụ, truy cập trực tiếp vào các thuộc tính hoặc phương thức riêng sẽ gây ra lỗi. Nhưng Python giữ khả năng truy cập vào chúng thông qua tên đã thay đổi. Ví dụ, bạn có thể truy cập vào thuộc tính riêng bằng cách sử dụng tên "bị biến dạng", như dưới đây:


class MyClass:
    def __init__(self):
        self.__private_attribute = "I am private"
   
obj = MyClass()
print(obj._MyClass__private_attribute)  # In ra: I am private

Mặc dù truy cập thông qua tên "bị biến dạng" là có thể, nhưng nên tránh điều này, vì nó vi phạm nguyên tắc đóng gói và có thể dẫn đến sự không ổn định của mã.

Để xem cách Python thay đổi tên của các thuộc tính, bạn có thể sử dụng hàm tích hợp dir(), hiển thị tất cả các thuộc tính và phương thức có sẵn của một đối tượng:


class MyClass:
    def __init__(self):
        self.__private_attribute = "I am private"
   
obj = MyClass()
print(dir(obj))  # In ra tất cả các thuộc tính và phương thức của đối tượng, bao gồm cả tên "bị biến dạng"

Kết quả của việc thực hiện hàm dir() bạn sẽ thấy danh sách tất cả các thuộc tính và phương thức của đối tượng, bao gồm _MyClass__private_attribute, xác nhận cơ chế "name mangling".

5.3 Tự động gọi các phương thức

Có một khía cạnh thú vị khi làm việc với các constructor mà có thể bạn đã chú ý. Phương thức __init__ đã được gọi tự động.

Thực ra có khá nhiều tình huống như vậy, và nhiều phương thức cho những tình huống này. Ví dụ:

Phương thức __str__

Nếu đối tượng của bạn có phương thức __str__, thì nó sẽ được gọi tự động khi cố gắng chuyển đổi đối tượng của bạn thành một chuỗi, ví dụ, khi sử dụng các hàm print()str().


class Cat:
    def __init__(self, name, age):
        self.name = name
        self.age = age
            
    def __str__(self):
        return f"{self.name} is {self.age} years old"
            

cat = Cat("Barsik", 5)
print(cat)  # In ra: Barsik is 5 years old

Phương thức __len__

Nếu đối tượng của bạn có phương thức __len__, thì nó sẽ được gọi tự động khi cố gắng xác định "độ dài" của đối tượng của bạn — được sử dụng bởi hàm len(). Ví dụ:


class MyList:
    def __init__(self, items):
        self.items = items
        
    def __len__(self):
        return len(self.items)
        
        
my_list = MyList([1, 2, 3])
print(len(my_list))  # In ra: 3

Các "phương thức đặc biệt" như vậy sẽ còn nhiều trong cuộc sống lập trình của bạn, nhưng làm việc với chúng thật là thú vị. Vì vậy, hãy chuẩn bị :)

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION