Câu 1Câu nào sau đây là cách định nghĩa class Python đúng?
Class và Instance — Định nghĩa kiểu của riêng bạn
Học class và instance trong Python từ con số không. Đi qua định nghĩa class, vai trò của self, và dùng __init__ để gán thuộc tính — kèm sơ đồ minh họa.
Như đã đề cập trong bài tổng kết trước, các kiểu có sẵn như int, str, list, và dict không thể trực tiếp biểu diễn các khái niệm nghiệp vụ như "người dùng", "sản phẩm", hay "đơn hàng". Bước tiếp theo là định nghĩa kiểu của riêng bạn với class — đó là cánh cửa vào lập trình hướng đối tượng (OOP).
Vì sao lại cần lập trình hướng đối tượng?
Tất cả những gì bạn viết đến giờ đều là lập trình thủ tục — kết hợp các hàm để điều khiển hành vi. Khi chương trình lớn dần, dữ liệu liên quan và logic xử lý chúng có xu hướng phân tán, và việc thay đổi cùng tái sử dụng trở nên khó khăn hơn.
Với lập trình hướng đối tượng (OOP), bạn gộp dữ liệu liên quan (thuộc tính) và các thao tác trên chúng (phương thức) vào một đơn vị duy nhất — một đối tượng. Điều đó cho phép bạn định hình code theo các khái niệm thế giới thực.
function(data). OOP đưa cả hai vào trong một object để chúng sống cùng nhau.Class là blueprint, Instance mới là thứ thật
Hai thuật ngữ neo giữ mọi thứ trong OOP:
- Class — một blueprint liệt kê các thuộc tính và phương thức mà bạn sẽ có
- Instance — thứ thật được tạo ra từ blueprint đó
Ví dụ, định nghĩa class Product để nắm bắt khái niệm sản phẩm, và bạn có thể tạo bao nhiêu instance sản phẩm riêng lẻ tùy thích — apple, banana, orange. Cùng nhau, class và instance được gọi là object.
Định nghĩa một class tối giản
Hãy thực sự định nghĩa một class Product cho các mặt hàng trong cửa hàng. Cú pháp là class ClassName:. Theo quy ước, tên class trong Python dùng CapitalizedCamelCase (Product, UserAccount, v.v.).
Một biến viết trực tiếp bên trong class — như name = "apple" — được xem là giá trị mặc định mà class giữ, và bạn có thể đọc nó qua tên class dưới dạng Product.name.
class Product:
name = "apple"
price = 150
# Truy cập trực tiếp vào class
print(Product.name) # apple
print(Product.price) # 150
class — name / price — gắn vào chính class và có thể đọc trực tiếp qua tên class dưới dạng Product.name.Các biến viết trực tiếp dưới class được gọi là class variable. Chúng hoạt động khác với instance variable (mà mỗi instance sở hữu riêng), nhưng ta sẽ quay lại sự phân biệt đó ở bài sau. Bây giờ, bạn chỉ cần ý tưởng tối giản: "gắn một giá trị vào chính class, rồi đọc nó dưới dạng Product.name."
Dùng __init__ để mỗi instance có giá trị riêng
Class tối giản ở trên chỉ gắn một giá trị cố định như name = "apple" vào class, nên mọi instance bạn tạo ra đều sẽ là "apple". Trong code thực tế, bạn muốn nhiều instance từ một class — apple, banana, orange — mỗi cái có giá trị riêng.
Công cụ cho việc này là phương thức đặc biệt __init__ của Python — còn gọi là constructor hay hàm khởi tạo. Khi bạn gọi Product("apple", 150) với đối số, Python tự động gọi __init__, và bên trong nó bạn viết self.name = name để lưu giá trị lên chính instance.
Hai dấu gạch dưới — Quy ước của Python
Tên được bao bởi hai dấu gạch dưới như __init__ được gọi là dunder method. Python gán cho chúng ý nghĩa đặc biệt và gọi chúng tự động vào những thời điểm nhất định — __init__ khi instance được tạo, __str__ khi cái gì đó được in ra, v.v. Ta sẽ tìm hiểu sâu hơn về __init__ và gặp __del__ ở bài tiếp theo.
Product("apple", 150) khiến Python ① chuẩn bị một instance rỗng, ② truyền nó vào __init__ dưới dạng self, và ③ ghi các đối số lên thuộc tính của instance đó — để lại cho bạn một object hoàn chỉnh.class Product:
def __init__(self, name, price): # Được gọi tự động khi instance được tạo
self.name = name
self.price = price
apple = Product("apple", 150)
banana = Product("banana", 80)
print(apple.name, apple.price) # apple 150
print(banana.name, banana.price) # banana 80
# apple và banana là các object khác nhau
print(apple is banana) # False
Product(...) đều tạo ra một object riêng biệt. apple, banana, và orange đều đến từ cùng blueprint nhưng là các object khác nhau, mỗi cái có name / price riêng.Cách phương thức và self hoạt động
Chỉ riêng thuộc tính thì chỉ "chứa dữ liệu" — không khác mấy so với một dict thông thường. Điểm thực sự của OOP là bạn còn có thể viết các thao tác trên instance cùng với dữ liệu. Những thao tác đó được gọi là phương thức.
def là một phương thức. Tham số đầu self là bắt buộc, và mỗi lần gọi Python điền vào nó instance mà bạn gọi phương thức từ đó. Bên trong phương thức, viết self.name để truy cập thuộc tính của instance đó.Khi bạn viết apple.show(), bên trong Python đang chạy Product.show(apple) — self cuối cùng giữ apple.
- apple và banana là các instance riêng biệt
- Khi bạn gọi
apple.show()…
- self được điền với apple tự động
self.nameđọc apple.name- Với
banana.show(),selftrở thànhbanana
show, gọi từ apple đọc name của apple, gọi từ banana đọc name của banana.class Product:
def __init__(self, name, price):
self.name = name
self.price = price
def show(self): # Tham số đầu luôn là self
print(f"{self.name}: ${self.price}")
apple = Product("apple", 150)
banana = Product("banana", 80)
apple.show() # apple: $150 bên trong tương đương Product.show(apple)
banana.show() # banana: $80 bên trong tương đương Product.show(banana)
apple.show() đặt apple vào self, còn gọi banana.show() đặt banana vào self. Cùng một phương thức show chạy với self khác nhau mỗi lần, tùy theo caller.self chỉ là quy ước
Về mặt cú pháp, tham số đầu có thể là gì cũng được — def show(this): cũng chạy. Nhưng gần như mọi codebase Python đều dùng self, nên hãy bám theo. Bất kỳ tên gì khác sẽ làm người đọc bối rối.
Trong bài này bạn đã nắm được căn bản của lập trình hướng đối tượng. Ta sẽ đi sâu vào biến và phương thức ở các bài sau.
Kiểm tra kiến thức
Hãy trả lời từng câu hỏi một.
Câu 2Cái gì được tự động truyền vào tham số đầu self của một phương thức khi được gọi?
Câu 3Đoạn code sau in ra gì?class P: def __init__(self, x): self.x = xa = P(10)b = P(20)print(a.x + b.x)