Trường ĐH Công nghệ – Đại học Quốc gia Hà Nội
Tài liệu đọc: Think Python 3rd ed., 15.{1,2,5,6}, 17.{1,2,5,8,9}
class Point3:
pass
p = Point3()
p.x = 2
p.y = 3
p.z = 5
p = Point3()
p.x = 2
p.y = 3
p.z = 5
p2 = Point3()
p2.eks = 2
p2.z = 'five'
class Point3:
def __init__(obj, x_val, y_val, z_val):
obj.x = x_val
obj.y = y_val
obj.z = z_val
point1 = Point3(2, 3, 5)
Phương thức initializer đảm bảo mọi đối tượng được khởi tạo nhất quán.
Lưu ý: __init__ có dấu gạch dưới kép ở cả hai bên, còn gọi là dunder (double underscore).
class Point3:
def __init__(self, x_val, y_val, z_val):
self.x = x_val
self.y = y_val
self.z = z_val
point1 = Point3(2, 3, 5)
class Post:
def __init__(self, text):
"""A post whose text is `text`
and with no likes so far."""
self.text = text
self.n_likes = 0
post1 = Post('good vibes')
Thường dùng cùng tên cho tham số & thuộc tính, ví dụ text.
Với lời gọi <ClassName>(<args>), Python làm:
| Thành phần | Cú pháp | Ghi chú |
|---|---|---|
| Định nghĩa lớp | class <ClassName>: ... | Tên lớp thường viết hoa chữ đầu. |
| Initializer | def __init__(self, ...): ... | Tên bắt buộc là __init__, tham số đầu là self. |
Chắc chắn là không! Hãy nhớ lại một số phương thức của list sau:
| Phương thức | Mô tả |
|---|---|
| lst.index(item) | Trả về chỉ số của lần xuất hiện đầu tiên của item trong lst. |
| lst.insert(i, item) | Chèn item vào lst ngay trước phần tử tại vị trí i, các phần tử bên phải bị dời sang phải. |
| lst.append(item) | Thêm item vào cuối lst. |
| lst1.extend(lst2) | Nối tất cả phần tử của lst2 vào sau lst1. |
Các phương thức dunder được dùng cho những nhiệm vụ đặc biệt, gắn với cơ chế
riêng của Python.
Phần lớn phương thức ta tự định nghĩa sẽ không phải là dunder.
class Counter:
def __init__(self):
self.count = 0
def currCount(self):
return self.count
def incr(self):
self.count = self.count + 1
def reset(self):
self.count = 0
>>> ctr = Counter()
>>> ctr.currCount()
0
>>> ctr.incr()
>>> ctr.currCount()
1
Gọi phương thức bằng cú pháp obj.method(...).
class Counter:
def __init__(self):
self.count = 0
def currCount(self):
return self.count
def incr(self):
self.count = self.count + 1
def reset(self):
self.count = 0
self luôn trỏ tới đối tượng nhận thông điệp.
class Counter:
def __init__():
self.count = 0
>>> ctr = Counter()
TypeError: Counter.__init__() takes 0 positional arguments but 1 was given
Python tự động truyền self; nếu quên khai báo tham số sẽ báo lỗi.
>>> Point3(2, 3, 5)
<__main__.Point3 object at 0x10377e990>
>>> range(0, 10, 2)
range(0, 10, 2)
class Point3:
def __repr__(self):
return 'Point3(' \
+ str(self.x) + ', ' \
+ str(self.y) + ', ' \
+ str(self.z) \
+ ')'
>>> Point3(2, 3, 5)
Point3(2, 3, 5)
__repr__ dành cho lập trình viên, dùng trong tương tác & debug.
class Point3:
def __str__(self):
return '(' \
+ str(self.x) + ', ' \
+ str(self.y) + ', ' \
+ str(self.z) \
+ ')'
>>> str(Point3(2, 3, 5))
'(2, 3, 5)'
>>> print(Point3(2, 3, 5))
(2, 3, 5)
class Account:
...
class InterestAccount(Account):
...
Ý nghĩa: InterestAccount thừa hưởng toàn bộ phương thức từ Account.
class Account:
def __init__(self, owner, number):
self._owner = owner
self._number = number
self._balance = 0
def deposit(self, amount):
self._balance += amount
def printStatement(self):
print('Owner: ' + self._owner)
print('Account Number: ' + str(self._number))
print('Balance: ' + str(self._balance))
class InterestAccount(Account):
def addInterest(self, pct_rate):
interest = self._balance * pct_rate
self.deposit(interest)
>>> acct = InterestAccount('Anne', '123')
>>> acct.deposit(1000)
>>> acct.addInterest(0.05)
>>> acct.printStatement()
Owner: Anne
Account Number: 123
Balance: 1050.0
Subclass sử dụng lại phương thức của superclass nhờ kế thừa.
class Account:
def __init__(self, owner, number):
self._owner = owner
self._number = number
self._balance = 0.0
class CreditAccount(Account):
def __init__(self, owner, number, limit):
super().__init__(owner, number)
self._limit = limit
super().__init__(...) khởi tạo phần trạng thái chung ở lớp cha.
class Account:
def __init__(self, owner, number):
self._owner = owner
self._number = number
self._balance = 0.0
class DepositAccount(Account):
# Không định nghĩa __init__ ở đây
pass
acct = DepositAccount('Anne', '2546154123')
Quy tắc bottom-up: DepositAccount không có __init__ → dùng của Account.
class C:
...
thực chất tương đương:
class C(object):
...
class object:
def __str__(self):
# trả về chuỗi kiểu
# '<class-name> object at <address>'
...
class C(object):
...
# không định nghĩa __str__()
>>> c = C()
>>> str(c)
<__main__.C object at 0x...>
Nếu lớp ta không định nghĩa __str__, Python dùng bản mặc định của object.
| Hàm | Mô tả | Ví dụ |
|---|---|---|
| isinstance(obj, cls) | Trả về True nếu obj là instance của cls hoặc bất kỳ subclass nào. | isinstance(a, Account) |
| type(obj) | Trả về lớp trực tiếp của đối tượng; không “leo” lên superclass. | type(a) == InterestAccount |
class Account:
def printStatement(self):
print('Statement:')
print('Account #: ' + self._number)
print('Owner: ' + self._owner)
print('Balance: $' + str(self._balance))
class CreditAccount(Account):
def printStatement(self):
print('Statement:')
print('Account #: ' + self._number)
print('Owner: ' + self._owner)
print('Balance: $' + str(self._balance))
print('Limit: $' + str(self._limit))
class Account:
def printStatement(self):
print('Statement:')
print('Account #: ' + self._number)
print('Owner: ' + self._owner)
print('Balance: $' + str(self._balance))
class CreditAccount(Account):
def printStatement(self):
super().printStatement()
print('Limit: $' + str(self._limit))
Gọi lại phiên bản superclass rồi thêm thông tin riêng của subclass.
class Account:
def __str__(self):
return 'Account #' + self._number
Giờ khi dùng str(acct), ta nhận chuỗi thân thiện hơn thay vì địa chỉ bộ nhớ.
class Point3:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
>>> Point3(0, 0, 0) == Point3(0, 0, 0) # 2 đối tượng khác nhau
False
>>> 42 == 42
True
Hai điểm có cùng tọa độ nhưng lại không “bằng nhau” theo mặc định.
| Khái niệm | Toán tử | Câu hỏi |
|---|---|---|
| Equality | is | Hai biến có trỏ tới cùng một đối tượng trong bộ nhớ không? |
| Equivalence | == | Hai đối tượng có tương đương về nội dung/ý nghĩa không? |
def incr(n):
return n + 1
>>> x = 1000
>>> y = incr(x) - 1
>>> x == y
True
>>> x is y
False
Python tùy biến == cho int để so sánh giá trị, không so sánh địa chỉ.
class object:
...
def __eq__(self, other):
return (self is other)
Với lớp tự định nghĩa, mặc định == giống hệt is.
class Point3:
...
def __eq__(self, other):
"""Same coordinates"""
return type(other) == Point3 \
and self.x == other.x \
and self.y == other.y \
and self.z == other.z
>>> Point3(0, 0, 0) == Point3(0, 0, 0)
True
Giờ equivalence cho Point3 là “cùng tọa độ”.
| Toán tử | Phương thức dunder |
|---|---|
| == | __eq__ |
| != | __ne__ |
| + | __add__ |
| - | __sub__ |
| and | __and__ |
| or | __or__ |
| < | __lt__ |
| <= | __le__ |
| > | __gt__ |
| >= | __ge__ |
| … và nhiều toán tử khác |
* Bạn không cần phải nhớ hết tên các phương thức này, chỉ cần nhớ __eq__ là đủ.