INTRODUCTION
FLOW CONTROL
FUNCTIONS
DATATYPES
OBJECT & CLASS
Bài tập Python: Lập trình hướng đối tượng (OOP) trong Python Lập trình hướng đối tượng trong Python Class Variables trong Python Tìm hiểu về Methods trong Python Cách sử dụng phương thức __init__() trong Python Các biến Instance trong Python Tìm hiểu về Class Attributes trong Python Hàm Static Methods trong Python Phương thức __str__ trong Python Phương thức __repr__ trong Python Phương thức eq trong Python Tìm hiểu phương thức __hash__ trong Python Phương thức __bool__ trong Python Phương thức del trong Python Tìm hiểu về lớp Property trong Python Tìm hiểu về nạp chồng toán tử trong Python Trình Decorator Property trong Python Thuộc tính chỉ đọc trong Python Thuộc tính Delete trong Python Sử dụng super() trong Python Sử dụng __slots__ trong Python Cách sử dụng Protocol trong Python Sử dụng Enum aliases và @enum.unique trong Python Tùy chỉnh và mở rộng lớp Enum trong Python Cách sử dụng hàm Auto() của Python Single Responsibility Principle trong Python Nguyên tắc Đóng-Mở trong Python Nguyên tắc thay thế Liskov - LSP trong Python Interface Segregation Principle - ISP trong Python. Nguyên tắc đảo ngược sự phụ thuộc trong Python Đa kế thừa trong Python Tìm hiểu về các lớp mixin trong Python Mô tả Descriptors trong Python Phân biệt Data Descriptor và Non-data Descriptor trong Python Phương thức __new__ trong Python Tìm hiểu về Class Type trong Python Lớp Metaclass trong Python Ví dụ sử dụng metaclass trong Python Tìm hiểu về decorator dataclass trong Python Tìm hiểu về các ngoại lệ trong Python Ngoại lệ Raise trong Python Sử dụng câu lệnh raise from trong Python Ngoại lệ tùy chỉnh trong Python Module trong Python Package trong Python Class trong Python Hàm khởi tạo trong Python Kế thừa trong Python Đa kế thừa trong Python Setter và Getter trong Python Override trong Python Interface trong Python Bài tập Python: Module và Class
ADVANCED TOPICS
BỔ SUNG
PYTHON CĂN BẢN
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.

Mô tả Descriptors trong Python

Trong bài viết này, bạn sẽ tìm hiểu descriptors trong Python—một cơ chế mạnh mẽ cho phép bạn tùy chỉnh cách các thuộc tính của lớp được truy cập và gán giá trị. Bằng cách hiểu cách descriptors hoạt động và cách triển khai chúng, bạn có thể quản lý các thuộc tính phức tạp một cách hiệu quả, đồng thời tối ưu hóa khả năng tái sử dụng mã và giảm thiểu sự lặp lại không cần thiết. Hãy cùng tìm hiểu cách sử dụng descriptors để cải thiện chất lượng mã Python của bạn.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Giới thiệu về Python Descriptors

Giả sử bạn có một lớp Person với hai thuộc tính là first_namelast_name:

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

Bạn muốn các thuộc tính first_namelast_name phải là các chuỗi không rỗng, nhưng với cách khai báo thông thường như trên, không có gì đảm bảo rằng các giá trị này sẽ hợp lệ.

Để đảm bảo tính hợp lệ của dữ liệu, bạn có thể sử dụng thuộc tính property kết hợp với các phương thức getter và setter, như sau:

Bài viết này được đăng tại [free tuts .net]

class Person:
    def __init__(self, first_name, last_name):
        self.first_name = first_name
        self.last_name = last_name

    @property
    def first_name(self):
        return self._first_name

    @first_name.setter
    def first_name(self, value):
        if not isinstance(value, str):
            raise ValueError('First name phải là chuỗi')
        if len(value) == 0:
            raise ValueError('First name không được để trống')
        self._first_name = value

    @property
    def last_name(self):
        return self._last_name

    @last_name.setter
    def last_name(self, value):
        if not isinstance(value, str):
            raise ValueError('Last name phải là chuỗi')
        if len(value) == 0:
            raise ValueError('Last name không được để trống')
        self._last_name = value

Trong lớp Person này, phương thức getter trả về giá trị của thuộc tính, trong khi phương thức setter kiểm tra tính hợp lệ trước khi gán giá trị cho thuộc tính.

Mặc dù cách này hoạt động tốt, nhưng nó lặp lại logic kiểm tra cho cả first_namelast_name. Nếu lớp có thêm nhiều thuộc tính cần kiểm tra chuỗi không rỗng, bạn sẽ phải lặp lại logic này nhiều lần.

Để tránh việc lặp lại code, bạn có thể tách phần logic kiểm tra ra một phương thức riêng. Tuy nhiên, Python cung cấp một giải pháp tốt hơn: sử dụng descriptors.

Định nghĩa một lớp Descriptor trong Python

Đầu tiên, hãy định nghĩa một lớp descriptor với ba phương thức là __set_name__, __get__, và __set__:

class RequiredString:
    def __set_name__(self, owner, name):
        self.property_name = name

    def __get__(self, instance, owner):
        if instance is None:
            return self
        return instance.__dict__.get(self.property_name)

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError(f'{self.property_name} phải là chuỗi')
        if len(value) == 0:
            raise ValueError(f'{self.property_name} không được để trống')
        instance.__dict__[self.property_name] = value

Sau đó, sử dụng lớp RequiredString trong lớp Person:

class Person:
    first_name = RequiredString()
    last_name = RequiredString()

Nếu bạn gán một chuỗi rỗng hoặc giá trị không phải chuỗi cho các thuộc tính first_name hoặc last_name, sẽ có lỗi xảy ra:

try:
    person = Person()
    person.first_name = ''
except ValueError as e:
    print(e)

Kết quả:

First name không được để trống

Bạn cũng có thể sử dụng lớp RequiredString này cho bất kỳ lớp nào khác có các thuộc tính yêu cầu giá trị chuỗi không rỗng.

Ngoài RequiredString, bạn có thể định nghĩa các descriptor khác để kiểm tra các kiểu dữ liệu khác như tuổi, email, hoặc số điện thoại. Đây chỉ là một ứng dụng đơn giản của descriptors.

Protocol của Descriptor trong Python

Trong Python, protocol của descriptor bao gồm ba phương thức:

  • __get__: Lấy giá trị của thuộc tính.
  • __set__: Gán giá trị cho thuộc tính.
  • __delete__: Xóa thuộc tính.

Ngoài ra, descriptor có thể có phương thức __set_name__ để thiết lập tên thuộc tính khi một lớp được tạo ra.

Descriptor là gì?

Descriptor là một đối tượng của lớp mà nó triển khai một trong những phương thức thuộc protocol của descriptor.

Có hai loại descriptors:

  • Data Descriptor: Là đối tượng của lớp triển khai các phương thức __set__ và/hoặc __delete__.
  • Non-data Descriptor: Là đối tượng chỉ triển khai phương thức __get__.

Cách Descriptor hoạt động

Hãy sửa đổi lớp RequiredString để in ra các tham số mỗi khi phương thức __set_name__, __get__, và __set__ được gọi:

class RequiredString:
    def __set_name__(self, owner, name):
        print(f'__set_name__ được gọi với owner={owner} và name={name}')
        self.property_name = name

    def __get__(self, instance, owner):
        print(f'__get__ được gọi với instance={instance} và owner={owner}')
        if instance is None:
            return self
        return instance.__dict__.get(self.property_name)

    def __set__(self, instance, value):
        print(f'__set__ được gọi với instance={instance} và value={value}')
        if not isinstance(value, str):
            raise ValueError(f'{self.property_name} phải là chuỗi')
        if len(value) == 0:
            raise ValueError(f'{self.property_name} không được để trống')
        instance.__dict__[self.property_name] = value

Khi bạn khởi tạo lớp Person, Python sẽ tự động gọi phương thức __set_name__ của các đối tượng descriptor first_namelast_name. Ví dụ:

class Person:
    first_name = RequiredString()
    last_name = RequiredString()

Kết quả:

__set_name__ được gọi với owner=<class '__main__.Person'> và name=first_name
__set_name__ được gọi với owner=<class '__main__.Person'> và name=last_name

Phương thức __set__

Khi bạn gán một giá trị mới cho descriptor, Python sẽ gọi phương thức __set__. Ví dụ:

person = Person()
person.first_name = 'John'

Kết quả:

__set__ được gọi với instance=<__main__.Person object> và value=John

Phương thức __set__ lưu trữ giá trị vào từ điển __dict__ của đối tượng instance.

Phương thức __get__

Khi bạn truy cập vào thuộc tính, Python sẽ gọi phương thức __get__. Ví dụ:

person = Person()
person.first_name = 'John'
print(person.first_name)

Kết quả:

__get__ được gọi với instance=<__main__.Person object> và owner=<class '__main__.Person'>
John

Phương thức __get__ trả về giá trị của thuộc tính từ từ điển __dict__ của đối tượng.

Kết bài

Descriptors là một công cụ mạnh mẽ trong Python, cho phép bạn kiểm soát và tùy chỉnh hành vi của thuộc tính trong các lớp thông qua việc triển khai các phương thức như __set__, __get__, và __delete__. Chúng giúp cải thiện khả năng tái sử dụng mã, đơn giản hóa quá trình xác thực dữ liệu và giảm thiểu sự lặp lại trong các lớp phức tạp. Việc nắm vững cách sử dụng descriptors không chỉ giúp mã nguồn của bạn trở nên linh hoạt và dễ bảo trì hơn mà còn mở ra những tiềm năng mới trong việc quản lý và tối ưu hóa dữ liệu trong Python.

Cùng chuyên mục:

Sử dụng câu lệnh raise from trong Python

Sử dụng câu lệnh raise from trong Python

Ngoại lệ tùy chỉnh trong Python

Ngoại lệ tùy chỉnh trong Python

Ngoại lệ Raise trong Python

Ngoại lệ Raise trong Python

Tìm hiểu về các ngoại lệ trong Python

Tìm hiểu về các ngoại lệ trong Python

Tìm hiểu về decorator dataclass trong Python

Tìm hiểu về decorator dataclass trong Python

Ví dụ sử dụng metaclass trong Python

Ví dụ sử dụng metaclass trong Python

Lớp Metaclass trong Python

Lớp Metaclass trong Python

Tìm hiểu về Class Type trong Python

Tìm hiểu về Class Type trong Python

Phương thức __new__ trong Python

Phương thức __new__ trong Python

Phân biệt Data Descriptor và Non-data Descriptor trong Python

Phân biệt Data Descriptor và Non-data Descriptor trong Python

Tìm hiểu về các lớp mixin trong Python

Tìm hiểu về các lớp mixin trong Python

Đa kế thừa trong Python

Đa kế thừa trong Python

Nguyên tắc đảo ngược sự phụ thuộc trong Python

Nguyên tắc đảo ngược sự phụ thuộc trong Python

Interface Segregation Principle - ISP trong Python.

Interface Segregation Principle - ISP trong Python.

Nguyên tắc thay thế Liskov - LSP trong Python

Nguyên tắc thay thế Liskov - LSP trong Python

Nguyên tắc Đóng-Mở trong Python

Nguyên tắc Đóng-Mở trong Python

Single Responsibility Principle trong Python

Single Responsibility Principle trong Python

Cách sử dụng hàm Auto() của Python

Cách sử dụng hàm Auto() của Python

Tùy chỉnh và mở rộng lớp Enum trong Python

Tùy chỉnh và mở rộng lớp Enum trong Python

Sử dụng Enum aliases và @enum.unique trong Python

Sử dụng Enum aliases và @enum.unique trong Python

Top