Dữ liệu của class Python: instance và class attribute

0/5 No hay votos

Reportar esta app

Description

Mỗi class thường chứa hai loại thành viên quan trọng: thành phần chứa dữ liệu và thành phần xử lý dự liệu. Trong Python, thành phần chứa dữ liệu được gọi là attribute . Có thể xem attribute của Python tương tự như biến của class trong các ngôn ngữ như C++/ Java hay C#

Python phân biệt hai loại attribute: instance attribute gắn với object và class attribute gắn với chính class.

Ví dụ về sử dụng attribute trong Python

Chúng ta bắt đầu với một ví dụ:

Tạo module book.py và viết code như sau:


1
2
3
4
5
6
7
8
9
10
11
class Book:
    """A class for e-book"""

b = Book()
b.title = 'Python programming'
b.authors = 'Donald Trump'
b.year = 2020
print(b.title, b.authors, b.year) # kết quả là 'Python programming Donald Trumo 2020'

b2 = Book()
print(b2.title) # lỗi, không có attribute title trong object b2

Bạn có thể thấy rất nhiều điều lạ ở đây. Dễ thấy nhất là class Book hoàn toàn trống trơn. Trong class này chỉ có mỗi docstring. Tuy nhiên sau khi tạo object b, bạn lại có thể dùng phép toán truy xuất phần tử (dot notation) b.title, b.authors, b.year, gán giá trị cho chúng và sau sử dụng lại chúng trong hàm print(). Rõ ràng bạn không hề tạo title, authors hay year trong khai báo Book.

Khi bạn tạo object b2 và thử truy xuất giá trị title (b2. title) thì lại gặp lỗi ” không tìm thấy atrtribute title trong object b2″

Khi bạn tạo object b2 và thử truy xuất giá trị title (b2.title) thì lại gặp lỗi “không tìm thấy attritube title trong object b2”.

title, authors, year được gọi là những attribute của object b ( nhưng không phải là attribute của b2).

Như vậy, trong Python, attribute là những biến có thể chứa giá trị đặc trưng cho một object. Nó được tạo hoàn toàn độc lập với khai báo class ( không cần chỉ định trong khai báo class). Một cách chính xác hơn, biến này đượcc gọi là instance attribue ( do liên quan đến object).

Giờ hãy cập nhật class Book như sau:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Book:
    """A class for e-book"""

    def __init__(self,
                    title: str,
                    authors: str = '',
                    publisher: str = '',
                    year: int = 2020,
                    edition: int = 1):
        """Hàm tạo của class"""
        self.title = title
        self.authors = authors
        self.publisher = publisher
        self.year = year
        self.edition = edition
     
    def to_string(self, brief = True):
        """Get the infor and make a formated string"""
        if brief:
            return f"{self.title} by {self.authors}"
        else:
            return f"{self.title} by {self.authors}, {self.edition} edition, {self.publisher}, {self.year}"

    def print(this, brief = False):
        """Print the book infor"""
        print(this.to_string(brief))

# sử dụng class Book
b1 = Book('Tự học lập trình Python')
b1.authors = 'Nhật Linh'
b1.publisher = 'Tự học ICT'
b1.year = 2021
b1.print()

b2 = Book('Python programming', 'Trump D.', 'The White house', 2020)
print(b2.title, b2.authors)

Trong trường hợp này chúng ta tạo attribute bên trong hàm tạo của class. Bạn có thể thấy rằng giờ cả b1 và b2 đều có chung tổ hợp attribute title, authors, publisher, year và edition. Giờ những biến này được gọi là những instance attribute của class book.

Tham khảo thêm ngay  Kiểu danh sách list trong Python

Instance attribute

Trong Python, biến thành viên của class được gọi là instance attribute. Đây là các giá trị đặc trưng cho từng object.

Ví dụ, class book xác định rằng, tất cả sách được đặc trưng bởi tổ hợp giá trị của tiêu đề, tác giả, nhà xuất bản, năm xuất bản và lần tái bản. Vậy cuốn sách thứ nhất có giá trị tổ hợp là ‘Tự học lập trình Python’ của tác giả ‘ Nhật Linh’ do ‘ Tự học ICT’ xuất bản năm 2020 và là lần xuất bản đầu tiên. Tổ hợp giá trị này đặc trưng cho riêng 1 cuốn sách (object) Cuốn sách khác sẽ có tổ hợp giá trị khác.

Khia báo instance attribute

Trong class book, các lệnh tạo ( và gán giá trị) biến thành viên của class phải viết trong hàm tạo như sau:


1
2
3
4
5
6
7
8
9
10
11
12
def __init__(self,
                title: str,
                authors: str = '',
                publisher: str = '',
                year: int = 2020,
                edition: int = 1):
    """Hàm tạo của class"""
    self.title = title
    self.authors = authors
    self.publisher = publisher
    self.year = year
    self.edition = edition

Nếu bạn xuất phát từ C++, Java hay C# sẽ thấy cách tạo biến thành viên trong Python hơi khác biệt:

  • Biến thành viên trong Python được tạo ra trong hàm tạo chứ không viế trong thân class. Biến được khai báo trong thân class lại thuộc về nhóm class instance ( chúng ta sẽ học sau).
  • Biến thành viên được khai báo cùng với tham số self sử dụng phép toán truy xuất thành viên, giống như các biến này đã có sẵn và bạn chỉ việc gán dữ liệu.

Trong Python, biến selft ( hay bất kỳ tên tham số nào) đứng đầu trong danh sách tham số của __init__ sẽ trỏ tới object vừa tạo ( bởi magic method__new__()). Phép toán truy xuất thành viên ( dấu chấm) trên object sử dụng một định danh mới và phép gán sẽ tự động tạo ra một biến thành viên.

Tên biến thành viên được đặt theo quy tắc đặt định danh chung của Python, cũng như theo quy ước đặt tên của biến ( cụ bộ và toàn cục).

Như vậy, self.title = ‘ A new book’ trong __init__() sẽ tạo ra biến thành viên title với giá trị ‘A new book’.

Tất cả các tham số còn lại của __init__() chính là để cung cấp giá trị ban đầu cho các biến thành viên.

Như trong ví dụ 1 bạn đã thấy, attribute không nhất thiết phải khai báo trong hàm tạo. Bạn có thể khai báo attribute sau khi tạo object. Chỉ có điều, khi này attribute đó chỉ tồn tại trên object cụ thể đó. Nếu bạn tạo ra object mới, nó sẽ không có attribute trong constructor, tất cả object tạo ra sẽ có chung tổ hợp attribute. Điều này phù hợp với khái niệm class/object trong lập trình hướng đối tượng. Do vậy, bạn luôn nên tạo instance attribute trong constructor.

Tham khảo thêm ngay  Kiểu dữ liệu tuple trong Python

Truy xuất instance attribue

Với các instance attribute tạo ra như trên, bạn có thể truy xuất nó qua tên object trong code ở ngoài class như sau:


1
2
3
4
5
6
b3 = Book('')
b3.title = 'Tự học lập trình Python'
b3.authors = 'Nhật Linh'
b3.publisher = 'Tự học ICT'
b3.year = 2021
b3.edition = 2

Việc truy xuất này là hai chiều, nghĩa là có thể gán giá trị hoặc đọc giá trị.

Đối với code ở bên trong class, cách truy cập là tương tự. Hãy xem phương thức to_string():


1
2
3
4
5
6
def to_string(self, brief = True):
    """Get the infor and make a formated string"""
    if brief:
        return f"{self.title} by {self.authors}"
    else:
        return f"{self.title} by {self.authors}, {self.edition} edition, {self.publisher}, {self.year}"

Tạm thời chúng ta chưa trình bày kỹ về fphuowng thức này.

Hãy nhìn cách sử dụng biến title, authors, edittion, publisher và year thông qua tham số self: self.title, self.authors, self.edition, self.publisher, self.year.

Bạn không được viết trực tiếp tên attribute mà bắt buộc phải thông qua biến self. Nó không có gì khác biệt so với khi truy xuất từ tên object bên ngoài class, cả về hình thức và bản chất. Tham số self thực chất là một object kiểu Book được Python tự động truyền khi gọi phương thức to_string().

Class attribue

Hãy hình dung yêu cầu sau: khi xây dựng class Book, làm thế nào để theo dõi số lượng object đã được tạo ra? Logic đơn giản nhất là tạo ra một biến đếm. Mỗi khi tạo một object mới thì tăng giá trị của biến đếm. Nếu biến đếm và việc tăng giá trị của biến đếm nằm goài class thì rất đơn giản. Nhưng nếu chúng ta cần tích hợp logic này vào chính class thì làm như thế nào?

Để ý rằng, một biến đếm cho muc đích trên không thể phụ thuộc vào từng object. Nói cách khác, tất cả các object đều phải sử dụng chung một biến đếm. Để tăng giá trị khi tạo object, phép cộng phải được thực hiện trong constructor.

Python cung cấp một công cụ cho những mục đích tương tự: class attribute. Hãy thay đổi class Book như sau:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Book:
    """A class for e-book"""
   
    count =
    def __init__(self,
                 title: str,
                 authors: str = '',
                 publisher: str = '',
                 year: int = 2020,
                 edition: int = 1):
        """Hàm tạo của class"""
        self.title = title
        self.authors = authors
        self.publisher = publisher
        self.year = year
        self.edition = edition
        self.__private = True
        Book.count += 1

Để ý dòng 4 có lệnh khởi tạo biến count = 0 và dòng 18 có phép cộng Book.count+=1.Trong class Python, count là một class attribute.

Tham khảo thêm ngay  Funtion (hàm) trong Python

Class attribute là một biến gắn liền với chính class, có thể được truy xuát từ các object và có giá trị chung cho tất cả các object.

Sự khác biệt giữa class attribute và instance attribute nằm ở chỗ: instance attribute gắn với từng object cụ thể và thể hiện trạng thái riêng của từng object, class attribute gắn với chính class và đặc trưng cho class hoặc đặc trưng chung cho mọi object.

Với biến count xây dựng như trên bạn có thể sử dụng nó thông qua tên class: Book.count. Bạn sử dụng lối viết này dù ở trong thân class hay bên ngoài class.


1
2
3
4
5
6
7
8
b1 = Book('Lập trình hướng đối tượng với Python', 'Nhật Linh', 'Tự học ict', 2022, 2)
print(Book.count)

b2 = Book(title = 'Nhập môn lập trình Python', authors= 'Nhật linh', publisher= 'Tự học ICT')
print(Book.count)

b3 = Book('')
print(b3.count)

Do đặc điểm của class attribute là sử dụng chung trong các object khác nhau, bạn cũng có thể truy xuất giá trị cảu count thông qua tên object: b3.count. Tuy nhiên, khi truy xuất qua tên object bạn không thay đổi được giá trị của class attribute. Nói chính xác hơn, thay đổi giá trị của class attribute qua tên object sẽ không lưu lại được.

Public, protected, private trong Python.

Trong các ngôn ngữ hướng đối tượng truyền thống như C++, Java, C# mỗi thành viên thuộc về một trong các mức truy cập:

(1) public: tự do truy cập, không giới hạn gì.

(2) protected: chỉ class con và trong nội bộ class mới có thể truy cập.

(3) private: giới hạn truy cập trong nội bộ class.

Trong Python không có khái niệm về kiểm soát truy cập các thành viên như vậy. Hoặc cũng có thể nói rằng mặc định mọi thành viên của class trong Python đều là public. Nghĩa là code ngoài class có thể tự do truy cập các thành viên này.

Để mô phỏng lại hiệu quả của việc kiểm soát truy cập, Python sử dụng loạt kỹ thuật có tên gọi là xáo trộn tên ( name mangling).

Kỹ thuật này quy ước rằng:

  • Nếu muốn biến chỉ được sử dụng trong nội bộ class và các class con ( mức truy cập là protected), tên biến cần bắt đầu là _ ( một dấu gạch chân).
  • Nếu muốn biến chỉ được sử dụng trong nội bộ class ( mức truy cập private), tên biến cần bắt đầu là __ ( hai dấu gạch chân).

Ví dụ bạn có thể khai báo biến protected và private trong constructor của lớp Book như sau:


1
2
self.__private = True # private instance attribute
self._protected = False # protected instance attribute

Thực ra loại kỹ thuật này không làm thay đổi được việc truy cập thành viên của class. Nó đơn thuần là chỉ báo để lập trình viên và IDE biết ý định sử dụng của thành viên đó.

Ví dụ, trong class Book như trên, thực tế biến __private không hề trở thành private. IDE sẽ trợ giúp che biến này trong phần nhắc code. Tuy nhiên bạn vẫn có thể truy xuất nó như bình thường: b3.__private = False.

Leave a Reply

Your email address will not be published. Required fields are marked *