Chúng ta thêm menu con bằng cách gọi phương thức add_cascade từ menu cha và truyền tham chiếu đến đối tượng menu con vào tham số menu. Như vậy các menu từ menu bar cho đến submenu đều được tạo ra từ lớp Menu.Chỉ khác là menu bar chính là menu gốc, được gắn vào cửa sổ chính.
fileMenu.add_separator()
Để hiển thị một separator (đường kẻ ngang) thì chúng ta gọi phương thức add_separator().
Popup menu
Popup menu hay còn được gọi là menu ngữ cảnh là menu được hiện ra khi click chuột lên cửa sổ.
Trong phương thức showMenu(), chúng ta gọi phương thức self.menu.post(x, y) để hiển thị popup menu tại vị trí được chỉ định trên cửa sổ. Tham số e là đối tượng của sự kiện click chuột.
Trong phần này chúng ta sẽ tìm hiểu về một số widget cơ bản trong Tkinter là Checkbutton, Label, Scale, và Listbox.
Widget là các thành phần cấu tạo nên một ứng dụng GUI. Widget rất đa dạng, có một số widget quan trọng cần phải có của bất kì nền tảng nào kể cả Tkinter ví dụ như button (nút bấm), check box hay scroll bar (thanh cuộn). Ngoài những widget cơ bản lập trình viên còn có thể tùy chỉnh widget của riêng mình.
Trong ví dụ trên chúng ta hiển thị một check button lên cửa sổ có chức năng hiện/ẩn tiêu đề cửa sổ khi check.
self.var = BooleanVar()
Ở trên chúng ta tạo ra một đối tượng BooleanVar, đối tượng này sẽ được kết nối với checkbutton, mỗi khi trạng thái check thay đổi thì giá trị của self.var cũng sẽ thay đổi.
Ở dòng trên chúng ta tạo một đối tượng Checkbutton. Chúng ta kết nối biến self.var thông qua tham số variable. Tham số command chỉ định phương thức nào sẽ được gọi khi check, ở đây là phương thức onClick().
cb.select()
Chúng ta sử dụng phương thức select() để thiết lập trạng thái cho check cho button.
if self.var.get() == True:
self.master.title("Checkbutton")
else:
self.master.title("")
Tại phương thức onClick() chúng ta cho hiện/ẩn tiêu đề cửa sổ thông qua phương thức master.title(), chúng ta kiểm tra check button có được check hay không thông qua thuộc tính self.var.
Label
Label dùng để hiển thị text hoặc hình ảnh. Trong ví dụ dưới đây, chúng ta sẽ dùng Label để hiển thị ảnh lên màn hình.
Tạo một đối tượng Scale. Hai tham số from_ và to là phạm vi giá trị của scale. Tham số command chỉ định phương thức sẽ được gọi khi di chuyển thanh cuộn.
Cũng giống như Checkbutton, ở đây chúng ta tạo label, một đối tượng IntVar và kết nối đối tượng đó với label bằng tham số textvariable. Giá trị của biến này sẽ được hiển thị trên label.
def onScale(self, val):
v = int(float(val))
self.var.set(v)
Khi di chuyển thanh cuộn, phương thức onScale() sẽ được gọi kèm theo giá trị hiện tại của Scale. Chúng ta chuyển đổi kiểu dữ liệu của tham số này từ sang float rồi sang int sau đó đặt làm giá trị của đối tượng IntVar và đoạn text hiển thị trên label sẽ thay đổi theo
Listbox
Listbox cho phép hiển thị một danh sách các item. Người dùng có thể chọn một hoặc nhiều item.
from tkinter.ttk import Frame, Label
from tkinter import Tk, BOTH, Listbox, StringVar, END
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Listbox")
self.pack(fill=BOTH, expand=1)
acts = ["Scarlet Johansson", "Rachel Weiss", "Natalie Portman", "Jessica Alba"]
lb = Listbox(self)
for i in acts:
lb.insert(END, i)
lb.bind("<<ListboxSelect>>", self.onSelect)
lb.pack(pady=15)
self.var = StringVar()
self.label = Label(self, text=0, textvariable=self.var)
self.label.pack()
def onSelect(self, val):
sender = val.widget
idx = sender.curselection()
value = sender.get(idx)
self.var.set(value)
root = Tk()
ex = Example(root)
root.geometry("300x250+300+300")
root.mainloop()
Trong ví dụ trên, chúng ta hiển thị một danh sách tên các nữ diễn viên trong Listbox. Tên được chọn sẽ được hiển thị trên một label.
lb = Listbox(self)
for i in acts:
lb.insert(END, i)
Chúng ta tạo một đối tượng Listbox và thêm các item vào bằng phương thức insert().
lb.bind("<<ListboxSelect>>", self.onSelect)
Khi chọn một item thì sự kiện <<ListboxSelect>> sẽ được thực thi. Chúng ta gán sự kiện này vào phương thức onSelect().
sender = val.widget
Trong phương thức onSelect() chúng ta lấy tham chiếu đến Listbox thông qua thuộc tính val.widget và gán vào biến sender.
idx = sender.curselection()
Phương thức curselection() lấy về chỉ số của item đang được chọn.
value = sender.get(idx)
self.var.set(value)
Từ chỉ số chúng ta lấy giá trị text của item bằng phương thức get(), sau đó gán vào label.
Trong phần này chúng ta sẽ tìm hiểu cách sử dụng layout trong Tkinter.
Trong Tkinter có hai loại widget, loại thứ nhất là các widget thường như nút bấm, textbox… loại thứ hai là containter, đây là những widget chứa các widget khác, chúng còn có một cái tên nữa là layout.
Trong Tkinter có 3 loại layout là pack, grid và place. Trong đó place là kiểu layout tự do, thuật ngữ gọi làAbsolute Positioning, tức là bạn sẽ phải tự quy định vị trí cũng như kích thước của các widget. Layout pack sắp xếp các widget của bạn theo chiều ngang và chiều dọc. Còn layout grid sắp xếp widget theo dạng bảng.
Thiết kế tự do
Thường thì khi thiết kế giao diện chúng ta dùng layout nhiều hơn là thiết kế theo kiểu tự do. Khi thiết kế theo kiểu tự do này chúng ta sẽ phải tự quy định vị trí và kích thước cho các widget trên màn hình, nếu kích thước cửa sổ thay đổi thì kích thước và vị trí của các widget không thay đổi. Thiết kế theo kiểu tự do cũng rất bất tiện khi bạn muốn thay đổi, thêm hay bớt các widget thì hầu như bạn sẽ phải sắp xếp lại toàn bộ widget.
Trong ví dụ trên, chúng ta hiển thị 3 ảnh lên cửa sổ.
from PIL import Image, ImageTk
Để hiển thị ảnh thì chúng ta có thể dùng module PhotoImage trong gói Tkinter nhưng module này rất hạn chế nên ở đây mình dùng module Image và ImageTk trong thư viện Pillow, thư viện này không có sẵn trong Python 3 mà bạn phải tự down về và cài vào.
Chúng ta tạo ra 2 frame, frame đầu tiên là layout chính dùng để chứa toàn bộ widget. Frame thứ hai chúng ta đặt như lên frame đầu tiên và cho kích thước dãn ra theo cả chiều ngang và chiều dọc. Hai button được đặt theo chiều ngang ở góc bên phải của cửa sổ.
Hai dòng code trên tạo ra Frame thứ hai, frame này sẽ chiếm phần lớn diện tích Frame chính. Tham số relief mô phỏng hiệu ứng hiển thị 3D của Frame, ngoài giá trị RAISED bạn có thể thử một số giá trị khác như FLAT, SUNKEN, GROOVE, và RIDGE. Tham số borderwidth hiển thị đường viền của frame, mặc định tham số này là 0 nên bạn ko thể thấy đường viền.
Ở hai dòng trên chúng ta tạo nút close và tham chiếu đến đối tượng closeButton. Phương thức pack() quy định cách hiển thị của button trên cửa sổ, tham số side=RIGHT sẽ đưa button vào vị trí bên phải, padx và pady duy định khoảng cách giữa button với các widget khác và với đường viền cửa sổ chính, ở đây khoảng cách là 5 pixel.
okButton.pack(side=RIGHT)
Nút OK cũng được tạo ra tương tự và cũng được đưa sang phía bên phải màn hình, sát bên cạnh nút Close với khoảng cách 5 pixel.
Tạo Form
Trong ví dụ dưới đây, chúng ta tiếp tục tìm hiểu về layout pack.
Chúng ta tạo một frame và đặt 2 widget vào đó, đó là một label và một entry. Label là widget dùng để hiển thị text, entry là widget tạo nhập text. Label có độ dài cố định còn entry thì có độ dài tự động thay đổi theo kích thước cửa sổ nhờ vào 2 tham số fill và expand. Frame thứ hai cũng tương tự.
Chúng ta tạo một Entry để hiển thị số cũng như kết quả tính toán. Tham số sticky quy định cách thức co dãn theo kích thước cửa sổ, W+E tức là co dãn theo chiều từ trái sang phải.
Nút cls được đặt tại vị trí hàng 1 cột 0, chỉ số hàng và cột bắt đầu từ 0.
self.pack()
Phương thức pack() hiển thị các widget lên màn hình và khởi tạo kích thước cho chúng. Nếu chúng ta chỉ gọi pack() mà không đưa các tham số vào như trên thì kích thước của các widget luôn luôn là kích thước vừa đủ để bao bọc lấy đoạn text bên trong, ngoài ra không có tham số truyền vào thì phương thức này sẽ đặt layout ở vị trí trên cùng của frame. Bạn có thể kéo dãn cửa sổ để thấy cách nó hoạt động.
Tkinter là một gói trong Python có chứa module Tk hỗ trợ cho việc lập trình GUI. Tk ban đầu được viết cho ngôn ngữ Tcl. Sau đó Tkinter được viết ra để sử dụng Tk bằng trình thông dịch Tcl trên nền Python. Ngoài Tkinter ra còn có một số công cụ khác giúp tạo một ứng dụng GUI viết bằng Python như wxPython, PyQt, và PyGTK.
Về ngôn ngữ Python
Python là một ngôn ngữ lập trình hướng đối tượng, python hoàn toàn tạo kiểu động, cấp phát bộ nhớ động. Mục đích ra đời của Python là cung cấp một ngôn ngữ lập trình có cấu trúc rõ ràng, sáng sủa, thuận tiện cho người mới học lập trình. Python được phát triển bởi Guido và Rossum. Phiên bản đầu tiên được phát hành vào năm 1991. Python được lấy cảm hứng từ ABC, Haskell, Java, Lisp, Icon và Perl. Python là một ngôn ngữ thông dịch, đa nền tảng. Một trong những đặc điểm độc nhất của Python là ngôn ngữ này không dùng đến dấu chấm phẩy, dấu mở-đóng ngoặc {} để kết thúc câu lệnh hay khối lệnh, mà cách duy nhất để nó nhận biết một lệnh là dấu thụt đầu dòng.
Hiện tại Python có hai dòng phiên bản là dòng 2.x và 3.x. Phiên bản Python mà series này sử dụng là phiên bản 3.5.1. Ngày nay Python được phát triển bởi một cộng đồng tình nguyện trên khắp thế giới, và lẽ dĩ nhiên nó là một phần mềm mã nguồn mở.
Update: Python phiên bản 2.7.18 được phát hành vào ngày 20/04/2020 là phiên bản cuối cùng của dòng 2.x, nghĩa là từ nay Python sẽ chỉ cập nhật phiên bản 3.x trở lên.
Đoạn code trên sẽ tạo ra một cửa sổ nhỏ trên màn hình.
from tkinter import Tk, Frame
Ở dòng trên chúng ta import hai lớp Tk và Frame từ gói Tkinter. Lớp Tk được dùng để tạo cửa sổ, lớp Frame dùng để quản lý các widget. Chúng ta sẽ tìm hiểu về widget ở các bài sau.
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent, background="white")
Chúng ta định nghĩa lớp Excamplekế thừa từ lớp Frame. Lớp Frame là một widget dùng để quản lý các widet khác – các lớp kiểu này được gọi chung là containter hoặc layout. Trong phương thức khởi tạo __init__() chúng ta gọi lại phương thức khởi tạo từ lớp Frame và đưa vào tham số màu nền.
self.parent = parent
Chúng ta dùng thuộc tính parent để lưu lại đối tượng root.
self.initUI()
Chúng ta định nghĩa phương thức initUI() dùng để tạo các widget trên cửa sổ.
self.parent.title("Simple")
Phương thức title() sẽ thay đổi tiêu đề cửa sổ.
self.pack(fill=BOTH, expand=1)
Phương thức pack() có tác dụng sắp xếp vị trí các widget trước khi gắn chúng lên cửa sổ chính. Tham số fill=BOTH sẽ dãn widget ra theo chiều ngang và chiều rộng, tức là widget đó sẽ chiếm toàn bộ không gian cửa sổ.
root = Tk()
Ở dòng trên chúng ta tạo một cửa sổ và gán vào biến root để quản lý. Cửa sổ luôn phải được tạo trước khi tạo các widget.
root.geometry("250x150+300+300")
Phương thức geometry() quy định kích thước cửa sổ và vị trí hiển thị trên màn hình. Hai tham số đầu tiên là kích thước cửa sổ, hai phương thức sau là vị trí của cửa sổ trên màn hình.
app = Example(root)
Dòng code trên chúng ta tạo một Frame và gắn nó vào cửa sổ chính.
root.mainloop()
Cuối cùng là phương thức mainloop(). Bạn cứ hiểu đơn giản là phải gọi phương thức này thì cửa sổ mới hiện lên màn hình và bắt đầu nhận các sự kiện để xử lý.
Hiển thị Button
Trong ví dụ dưới đây, chúng ta sẽ tạo nút Quit, khi bấm vào nút đó thì thoát chương trình.
Chúng ta sẽ đặt một nút bấm trên cửa sổ, bấm vào nút đó thì thoát chương trình.
from tkinter.ttk import Frame, Button, Style
Một số widget trong Tkinter được hỗ trợ hiển thị màu sắc hoa lá khác nhau, thuật ngữ gọi là theme. Để sử dụng theme thì chúng ta import module ttk vào.
Để quy định kiểu theme thì chúng ta khởi tạo đối tượng Style() và gọi tới phương thức theme_use(). Một số kiểu theme mà mình biết là clam, default, alt, và classic.
Ở dòng trên chúng ta tạo một đối tượng widget là Button. Tham số đầu tiên là đối tượng container của nó. Tham số thứ hai là đoạn text sẽ hiển thị trên nút. Tham số thứ ba là lệnh được thực thi khi nút được bấm, ở đây là phương thức quit() tức là thoát chương trình.
quitButton.place(x=50, y=50)
Phương thức place() để quy định vị trí của nút bấm trên cửa sổ.
Trong phần này chúng ta sẽ tìm hiểu một số hàm kiểm tra trạng thái.
Trong Pythonmọi thứ đều là đối tượng. Một đối tượng thì có các thuộc tính và phương thức. Chúng ta có thể dùng các hàm kiểm tra để biết các thuộc tính và phương thức của một đối tượng.
Hàm dir()
Hàm dir() trả về một danh sách các thuộc tính và phương thức có trong một đối tượng.
Trong ví dụ trên chúng ta dùng hàm dir() để xem các thuộc tính và phương thức có trong một tuple.
>>> print (().__doc__)
tuple() -> empty tuple
tuple(iterable) -> tuple initialized from iterable's items
If the argument is a tuple, the return value is the same object.
Trong đó có thuộc tính __doc__, chúng ta có thể dùng hàm print() để xem nội dung của thuộc tính này.
Trong ví dụ trên chúng ta kiểm tra phiên bản Python, tên hệ điều hành và các đường dẫn thư mục liên quan.
Chúng ta sẽ kiểm tra một số thuộc tính khác trong module sys. Bạn cũng có thể dùng hàm dir() để lấy về danh sách đầy đủ các thuộc tính và phương thức có trong module sys.
Trong ví dụ trên chúng ta kiểm tra 3 thuộc tính là maxsize, executable, và argv.
>>> sys.maxsize
2147483647
Thuộc tính maxsize lưu trữ giá trị số nguyên lớn nhất được hỗ trợ trong Python.
>>> sys.executable
'C:\\python\\python.exe'
Thuộc tính executable trả về đường dẫn đầy đủ đến trình thông dịch Python.
>>> sys.argv
['']
Thuộc tính argv trả về tham số được đưa vào trình thông dịch Python. Ở đây là danh sách rỗng, nếu bạn chạy từ file script thì tham số này sẽ có ít nhất đường dẫn đến file script của bạn.
Hàm hasattr() kiểm tra xem một đối tượng nào đó có chứa thuộc tính nào đó không. Hàm getattr() sẽ trả về nội dung của thuộc tính đó nếu có.
True
True
True
The most base type
None
Hàm isinstance kiểm tra xem một đối tượng có thuộc một lớp nào đó không.
class MyObject():
def __init__(self):
pass
o = MyObject()
print (isinstance(o, MyObject))
print (isinstance(o, object))
print (isinstance(2, int))
print (isinstance('str', str))
Chúng ta đã biết là tất cả mọi thứ trong Python đều là đối tượng, kể cả số hay chuỗi. Tất cả các lớp trong Python đều kế thừa từ một lớp cơ sở có tên là object. Do đó hàm instance sẽ trả về true nếu chúng ta kiểm tra xem một đối tượng có thuộc lớp object hay không.
True
True
True
True
Hàm issubclass() kiểm tra xem một lớp có được kế thừa từ một lớp khác hay không. Nếu chúng ta dùng hàm issubclass() với 2 tham số giống nhau thì cũng trả về True, nói cách khác trong Python thì một lớp cũng là lớp con của chính nó.
Trong ví dụ trên, chúng ta định nghĩa lớp Wall kế thừa từ lớp Object.
True
False
True
True
Thuộc tính __doc__ là đoạn chuỗi mô tả về một đối tượng nào đó. Thuộc tính __name__ là chuỗi chứa tên của một đối tượng nào đó.
def noaction():
'''Blah blah blah'''
pass
funcs = [noaction, len, str]
for i in funcs:
print (i.__name__)
print (i.__doc__)
print ("-" * 75)
Trong ví dụ trên, chúng ta tạo một list chứa tên các hàm, trong đó có một hàm do chúng ta tự định nghĩa, 2 hàm còn lại có sẵn. Sau đó chúng ta duyệt qua list này và in nội dung hai thuộc tính __name__ và __doc__ của mỗi hàm ra.
noaction
Blah blah blah
---------------------------------------------------------------------------
len
len(object) -> integer
Return the number of items of a sequence or mapping.
---------------------------------------------------------------------------
str
str(object) -> string
Return a nice string representation of the object.
If the argument is a string, the return value is the same object.
---------------------------------------------------------------------------
Cuối cùng là hàm callable(), hàm này kiểm tra xem một đối tượng có phải là một hàm hay không.
class Car:
def setName(self, name):
self.name = name
def fun():
pass
c = Car()
print (callable(fun))
print (callable(c.setName))
print (callable([]))
print (callable(1))
Trong ví dụ trên chúng ta kiểm tra 4 đối tượng fun, c.setName, [] (list rỗng) và số 1 có phải là các hàm hay không.
print (callable(fun))
print (callable(c.setName))
Chắc chắn fun chính là một hàm do chúng ta tự định nghĩa, setName() là một phương thức (hàm nằm trong một lớp) của lớp Car.
Trong phần này chúng ta sẽ tìm hiểu về Iterator và Generator.
Iterator là một khái niệm chỉ các lớp dạng danh sách cho phép chúng ta duyệt qua chúng mà không cần quan tâm đến kiểu dữ liệu đó là gì.
Để một lớp được gọi là Iterator thì lớp đó phải hỗ trợ 2 phương thức là __iter__() và __next__(). Cả 2 phương thức đó được gọi chung là giao thức iterator (Iterator Protocol). Trong đó phương thức __iter__() chỉ đơn giản là trả về đối tượng tham chiếu tới chính nó, còn phương thức __next__() trả về phần tử tiếp theo có trong danh sách cho đến phần tử cuối cùng thì giải phóng exception StopIteration.
Hầu hết các kiểu dữ liệu dạng danh sách trong Python như kiểu list, tuple, string, dictionary đều hỗ trợ giao thức Iterator.
Trong đoạn code trên, chúng ta sử dụng tính tăng iterator của một string. Hàm iter() sẽ gọi đến phương thức __iter__() có sẵn trong lớp string để lấy về đối tượng tham chiếu đến chính nó. Phương thức __next__() sẽ trả về phần tử tiếp theo trong danh sách. Ngoài ra bạn còn có thể dùng các hàm như list(), tuple() để tạo các đối tượng danh sách mới dựa trên một iterator.
f
o
r
['m', 'i', 'd', 'a', 'b', 'l', 'e']
Trong ví dụ dưới đây, chúng ta sẽ viết một lớp riêng có hỗ trợ giao thức Iterator.
class seq:
def __init__(self):
self.x = 0
def __next__(self):
self.x += 1
return self.x**self.x
def __iter__(self):
return self
s = seq()
n = 0
for i in s:
print (i)
n += 1
if n > 10:
break
Trong đoạn code trên, chúng ta tạo một dãy số dài vô tận. Khi chúng ta sử dụng vòng lặp for, vòng lặp sẽ tự động gọi hàm iter() lên đối tượng của chúng ta để lấy về một đối tượng tham chiếu tới chính nó, và cứ mỗi lần lặp nó lại gọi đến phương thức __next__() đế lấy phần tử tiếp theo trong dãy.
Trong ví dụ dưới đây, chúng ta sẽ ngắt vòng lặp bằng cách giải phóng exception StopIteration.
class seq14:
def __init__(self):
self.x = 0
def __next__(self):
self.x += 1
if self.x > 14:
raise StopIteration
return self.x**self.x
def __iter__(self):
return self
s = seq14()
for i in s:
print (i)
Trong đoạn code trên chúng ta in ra 14 số đầu tiên trong dãy.
if self.x > 14:
raise StopIteration
StopIteration sẽ ngắt vòng lặp.
Generator
Generator chẳng qua cũng chỉ là Iterator, chúng cũng tạo ra một đối tượng kiểu danh sách, nhưng bạn chỉ có thể duyệt qua các phần tử của generator một lần duy nhất vì generator không lưu dữ liệu trong bộ nhớ mà cứ mỗi lần lặp thì chúng sẽ tạo phần tử tiếp theo trong dãy và trả về phần tử đó.
Đoạn code dưới đây ví dụ cách định nghĩa một generator.
def gen():
x, y = 1, 2
yield x, y
x += 1
yield x, y
it = gen()
print (it.__next__())
print (it.__next__())
try:
print (it.__next__())
except StopIteration:
print ("Iteration finished")
Để định nghĩa một generator thì chúng ta dùng từ khóa def giống như khi định nghĩa một hàm. Trong generator chúng ta dùng từ khóa yield để trả về các phần tử.
Từ khóa yield có chức năng y hệt như từ khóa return, chỉ khác ở chỗ là khi bạn gọi phương thức __next__() lần thứ nhất thì generator thực hiện các công việc tính toán giá trị rồi gặp từ khóa yield nào thì nó sẽ trả về các phần tử tại ví trí đó, khi bạn gọi phương thức __next__() lần thứ hai thì generator không bắt đầu chạy tại vị trí đầu tiên mà bắt đầu ngay phía sau từ khóa yield thứ nhất. Cứ như thế generator tạo ra các phần tử trong dãy, cho đến khi không còn gặp từ khóa yield nào nữa thì giải phóng exception StopIteration.
Trong phần này chúng ta sẽ tìm hiểu về lỗi ngoại lệ (exception) trong Python.
Lỗi exception là lỗi xảy ra trong quá trình chạy (tiếng anh là run time error). Trong lập trình có 3 loại lỗi là lỗi biên dịch, lỗi ngữ nghĩa và lỗi ngoại lệ. Trong đó lỗi biên dịch dễ phát hiện nhất, lỗi ngoại lệ là lỗi khó phát hiện nhất. Ví dụ như bạn viết một chương trình máy tính bỏ túi gồm các chức năng cộng trừ nhân chia. Thoạt nhìn có vẻ như rất đơn giản, chỉ cần yêu cầu người dùng nhập vào 2 số và phép tính, thực hiện phép tính rồi trả lại kết quả cho người dùng. Nhưng chương trình này sẽ báo lỗi nếu người dùng thực hiện một phép tính chia với số chia là 0. Đó là loại lỗi chỉ xảy ra trong khi chương trình chạy, để phòng ngừa loại lỗi này thì chỉ có một cách là các lập trình viên phải đoán trước các trường hợp lỗi ngoại lệ có thể xảy ra và xử lý chúng trước trong code của mình. Nhưng vì bạn chẳng phải nhà tiên tri, bạn không thể nào đoán trước tất cả mọi thứ được 🙂 nên trên thực tế thì các phần mềm lớn vẫn thường có lỗi chứ không có phần mềm nào không có lỗi cả, công việc của chúng ta là cố gắng phát hiện lỗi và sửa lỗi thôi.
Trong Python và các ngôn ngữ lập trình cấp cao khác như Java, C#… các lỗi exception thường gặp được định nghĩa thành một lớp riêng, mỗi khi chương trình gặp các lỗi này trình thông dịch sẽ báo tên các lỗi đó ra màn hình.
>>> 3 / 0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
Trong ví dụ trên, chúng ta chạy trình thông dịch Python trong cmd và thực hiện phép tính 3 / 0. Chương trình trả về lỗi ZeroDivisionError.
def input_numbers():
a = float(input("Enter first number:"))
b = float(input("Enter second number:"))
return a, b
x, y = input_numbers()
print ("%d / %d is %f" % (x, y, x/y))
Trong đoạn code trên, chúng ta tính phép chia 2 số, nếu một trong 2 số là số 0 thì sẽ nảy sinh một lỗi exception.
Enter first number:2
Enter second number:0
Traceback (most recent call last):
File "./zerodivision.py", line 12, in <module>
print "%d / %d is %f" % (x, y, x/y)
ZeroDivisionError: float division
Để xử lý lỗi exception thì chúng ta có 2 cách.
def input_numbers():
a = float(input("Enter first number:"))
b = float(input("Enter second number:"))
return a, b
x, y = input_numbers()
while True:
if y != 0:
print ("%d / %d is %f" % (x, y, x/y))
break
else:
print ("Cannot divide by zero")
x, y = input_numbers()
Cách thứ nhất là chúng ta kiểm tra bằng các câu lệnh if...else. Cứ thấy người dùng nhập vào số 0 thì chúng ta in câu thông báo và yêu câu nhập lại, không thì thực hiện phép tính và trả kết quả.
Enter first number:4
Enter second number:0
Cannot divide by zero
Enter first number:5
Enter second number:0
Cannot divide by zero
Enter first number:5
Enter second number:6
5 / 6 is 0.833333
Cách thứ hai là “bắt” các đối tượng exception có thể nảy sinh trong Python.
def input_numbers():
a = float(raw_input("Enter first number:"))
b = float(raw_input("Enter second number:"))
return a, b
x, y = input_numbers()
while True:
try:
print ("%d / %d is %f" % (x, y, x/y))
break
except ZeroDivisionError:
print ("Cannot divide by zero")
x, y = input_numbers()
Để bắt exception thì chúng ta sử dụng câu lệnh try...except. Trong ví dụ trên, sau từ khóa try là các câu lệnh thực hiện các công việc của chúng ta. Sau câu lệnh except là tên exception có thể xảy ra. Bên trong except là các câu thông báo lỗi mà chúng ta muốn in ra. Khi chạy ban đầu chương trình sẽ thực thi các câu lệnh bên trong try, nếu có exception xảy ra thì câu lệnh except sẽ “bắt” exception đó rồi thực hiện phần thông báo lỗi.
Bạn có thể “bắt” nhiều exception bằng cách dùng nhiều câu lệnh except hoặc đặt các exception bên trong một tuple.
Tham số đối tượng exception
Khi trình thông dịch báo lỗi exception, thì một đối tượng của lớp exception đó được tạo ra và bạn có thể tham chiếu đến nó bằng cách đặt một tham số thứ 2 vào sau tên của exception để lấy tham chiếu đến đối tượng này (thường chúng ta đặt tên là e hoặc exception).
Trong đối tượng exception có một thuộc tính tên là message. Đây là câu thông báo lỗi có sẵn trong Python.
Cannot divide by zero
Message: integer division or modulo by zero
Class: <type 'exceptions.ZeroDivisionError'>
Cấu trúc phân cấp của exception
Hệ thống exception cũng được phân cấp, với lớp gốc là lớp Exception.
try:
while True:
pass
except KeyboardInterrupt:
print ("Program interrupted")
Trong đoạn code trên chúng ta có một vòng lặp vô tận. Nếu chúng ta bấm Ctrl+C, vòng lặp sẽ bị ngắt bởi lỗi KeyboardInterrupt.
Exception
BaseException
KeyboardInterrupt
Lớp KeyboardInterrupt là lớp kế thừa từ lớp BaseException.
try:
while True:
pass
except BaseException:
print ("Program interrupted")
Chúng ta có thể bắt lỗi BaseException thay vì bắt lỗi KeyboardInterrupt.
Định nghĩa exception
Chúng ta có thể định nghĩa các lớp exception của riêng chúng ta.
class BFoundError(Exception):
def __init__(self, value):
print ("BFoundError: b character found at position %d" % value)
string = "You make me want to be a better man."
pos = 0
for i in string:
if i == 'b':
raise BFoundError(pos)
pos = pos + 1
Các lớp exception mới đều phải được kế thừa từ lớp Exception. Để giải phóng một exception thì chúng ta dùng từ khóa raise. Trong ví dụ trên, chúng ta định nghĩa lớp BFoundError, một string và một vòng lặp, chúng ta duyệt qua string, nếu tìm thấy kí tự b trong chuỗi thì giải phóng exception.
BFoundError: b character found at position 20
Traceback (most recent call last):
File "C:\Users\PhoCode\python\b.py", line 16, in <module>
raise BFoundError, pos
__main__.BFoundError
Từ khóa finally
Sau khối lệnh except chúng ta có thể có từ khóa finally và một số câu lệnh. Các câu lệnh trong khối finally sẽ được thực thi cho dù có hay không có lỗi exception nào xảy ra. Thường thì các câu lệnh trong finally được dùng để thực hiện các công việc giải phóng tài nguyên…
f = None
try:
f = file('indaclub', 'r')
contents = f.readlines()
for i in contents:
print (i,)
except IOError:
print ('Error opening file')
finally:
if f:
f.close()
Đoạn code trên sẽ mở một file, nếu vì một lý do gì đó mà file không thể mở được thì lỗi IOError sẽ xảy ra. Và cho dù có lỗi hay không có lỗi xảy ra thì chúng ta vẫn làm công việc đóng file trong phần finally.
Trong phần này chúng ta sẽ tìm hiểu về module trong Python.
Module trong Python rất đơn giản, đó là các file chứa code python có đuôi mở rộng là .py. Dùng module là một cách tuyệt vời để quản lý code Python, ngoài module ra thì chúng ta còn có các cách sau:
Dùng Hàm
Dùng Lớp
Dùng Module
Dùng Package
Trong đó quy mô nhỏ nhất là hàm và lớn nhất là Package. Bạn có thể hình dung chúng ta viết hàm để chia nhỏ các công việc ra, sau đó gộp các hàm liên quan với nhau vào một lớp cụ thể, các lớp có liên quan với nhau lại được gộp vào một file làm một module, cứ thế chương trình lớn dần và chúng ta lại gộp các module thành một package. Chúng ta sẽ tìm hiểu về package ở bài sau.
Đặt tên module
Tên module là tên file có phần mở rộng là .py. Ví dụ như chúng ta có file empty.py thì empty là tên module. Mặc nhiên module này có một biến tên là __name__, biến này lưu trữ tên của module đang được sử dụng. Khi chúng ta viết một file Python rồi chạy thì biến thì tên module của file này được gọi là __main__.
Trong ví dụ dưới đây chúng ta có 2 file: empty.py và modulename.py. Khi chúng ta chạy từ file modulename.py thì file này được gọi là module main. Module main sử dụng module của file empty.py. Để sử dụng một module thì chúng ta dùng từ khóa import.
Trong đoạn code trên chúng ta sử dụng 2 module, thứ nhất là module sys có sẵn trong Python và thứ hai là module empty do chúng ta tự tạo ra. Chúng ta in tên của các module này ra màn hình
__main__
empty
sys
Tên của module main sẽ luôn luôn là __main__. Các module khác sẽ là tên file.
Thư mục chứa module
Khi chúng ta import một module nào đó. Trình thông dịch sẽ tìm các module có sẵn trong Python. Nếu không thấy, nó sẽ tìm trong biến sys.path, biến này lưu trữ một list các chuỗi đường dẫn bao gồm đường dẫn đến thư mục hiện tại của file và đường dẫn đến thư mục cài đặt Python cùng với một số thư mục con bên trong. Nếu không tìm thấy module thì báo lỗi ImportError.
Như chúng ta đã biết từ khóa này được dùng để tích hợp một module vào chương trình.
from module import *
Cú pháp trên có ý nghĩa là tích hợp tất cả những gì có trong module vào chương trình của chúng ta ngoại trừ các đối tượng có tên bắt đầu bằng dấu gạch dưới _. Những đối tượng này được sử dụng với tên của module chứ không thể được gọi riêng như những đối tượng khác.
from math import *
print (cos(3))
print (pi)
Đoạn code trên tích hợp tất cả các đối tượng có thể sử dụng trong module math vào chương trình. Chúng ta có thể gọi các hàm trong module này một cách trực tiếp chứ không cần phải thông qua tên module.
-0.9899924966
3.14159265359
Cách import kiểu này có thể làm rối loạn chương trình của chúng ta vì có thể có rất nhiều module trùng tên với nhau, do đó chúng sẽ bị ghi đè.
from math import *
pi = 3.14
print (cos(3))
print (pi)
Đoạn code trên sẽ in ra con số 3.14. Vì trình thông dịch nghĩ rằng chúng ta muốn sử dụng biến pi do chúng ta tự gán chứ không phải biến pi có sẵn trong module math.
Ví dụ dưới đây kiểm nghiệm các hàm không được tích hợp vào chương trình bằng cách sử dụng kiểu import trên.
"""
names is a test module
"""
_version = 1.0
names = ["Paul", "Frank", "Jessica", "Thomas", "Katherine"]
def show_names():
for i in names:
print (i)
def _show_version():
print (_version)
from names import *
print (locals())
show_names()
Biến _version và hàm _show_version() sẽ không được tích hợp vào module private vì tên của chúng có dấu gạch dưới ở đầu. Chúng ta dùng hàm locals() để in ra các module đã được tích hợp.
{'__builtins__': <module '__builtin__' (built-in)>, '__file__': './private.py',
'show_names': <function show_names at 0xb7dd233c>,
'names': ['Paul', 'Frank', 'Jessica', 'Thomas', 'Katherine'],
'__name__': '__main__', '__doc__': None}
Paul
Frank
Jessica
Thomas
Katherine
from module import fun, var
Kiểu cú pháp trên chỉ tích hợp một số đối tượng được chỉ định vào module của chúng ta.
from math import sin, pi
print (sin(3))
print (pi)
Trong ví dụ trên chúng ta tích hợp 2 đối tượng sin và pi từ module math.
from names import _version, _show_version
print (_version)
_show_version()
Với kiểu cú pháp này chúng ta có thể tích hợp các module có tên có dấu gạch dưới ở đầu.
1.0
1.0
import module
Cú pháp cuối cùng này là cú pháp được sử dụng nhiều nhất. Bạn có thể dùng tất cả các đối tượng có trong module và để sử dụng thì bạn phải ghi tên module cùng với dấu chấm rồi mới đến tên đối tượng. Nhờ đó mà chương trình của bạn không bị rối tung lên.
import math
pi = 3.14
print (math.cos(3))
print (math.pi)
print (math.sin(3))
print (pi)
Trong ví dụ trên chúng ta import module math. Chúng ta có thể truy xuất được cả biến pi của chúng ta và biến pi trong module math.
-0.9899924966
3.14159265359
0.14112000806
3.14
import math as m
print (m.pi)
print (m.cos(3))
Ngoài ra khi import một module nào đó, bạn có thể muốn dùng một cái tên khác ngắn gọn hơn bằng cách thêm từ khóa as cùng với tên mà bạn muốn dùng. Tên này chỉ có hiệu lực trong file mà bạn đang viết.
3.14159265359
-0.9899924966
Nếu bạn import sai tên module thì lỗi ImportError sẽ nảy sinh.
Trong ví dụ trên, chúng ta không có module nào tên là empty2 do đó trình thông dịch báo lỗi.
Failed to import: No module named empty2
Thực thi module
Để sử dụng module thì chúng ta import nó từ chương trình của chúng ta nhưng chúng ta cũng có thể chạy chính nó. Ngoài các module có sẵn trong Python còn có rất nhiều lập trình viên viết các module khác cho Python. Thường thì sau khi viết xong họ hay viết những đoạn code để kiểm tra module của mình ở cuối file.
Chúng ta sẽ kiểm chứng điều này bằng cách viết module in dãy số fibonacci. Dãy số fibbonacci là dãy số mà số thứ i bằng tổng của số thứ i-1 và i-2, ngoại trừ hai số đầu tiên có giá trị là 1.
"""
A module containing the fibonacci
function.
"""
def fib(n):
a, b = 0, 1
while b < n:
print (b,)
(a, b) = (b, a + b)
# testing
if __name__ == '__main__':
fib(500)
Chúng ta sẽ chạy module này từ command prompt.
1 1 2 3 5 8 13 21 34 55 89 144 233 377
Trong đoạn code trên chúng ta có câu lệnh if để kiểm tra, nếu module được chạy từ trình biên dịch thì nó sẽ tự thực thi câu lệnh fib(500). Còn nếu được import từ module khác thì không.
Trong phần này chúng ta sẽ tìm hiểu về lập trình hướng đối tượng trong Python.
Lập trình hướng đối tượng (gọi tắt là OOP, từ chữ Anh ngữ object-oriented programming), là kĩ thuật lập trình hỗ trợ công nghệ đối tượng. OOP được xem là giúp tăng năng suất, đơn giản hóa độ phức tạp khi bảo trì cũng như mở rộng phần mềm bằng cách cho phép lập trình viên tập trung vào các đối tượng phần mềm ở bậc cao hơn. Ngoài ra, nhiều người còn cho rằng OOP dễ tiếp thu hơn cho những người mới học về lập trình hơn là các phương pháp trước đó (theo Wikipedia).
Trong OOP có các khái niệm cơ bản sau đây:
Trừu tượng (Abstraction)
Đa hình (Polymorphism)
Đóng gói (Encapsulation)
Kế thừa (Inheritance)
Các khái niệm trên giải thích theo wikipedia hay lấy trong sách rất khó hiểu nên mình sẽ làm ví dụ luôn và tới đoạn nào mình sẽ giải thích đoạn đó cho dễ hiểu.
Khái niệm đối tượng
Đối tượng là các thực thể của một lớp nào đó. Lớp là khuôn mẫu của các đối tượng. Ví dụ như list, tuple, dictionary, string, int… là các lớp. Khi chúng ta khai báo biến thuộc các lớp này thì chúng là các đối tượng. Tất cả mọi thứ trong Python đều là đối tượng.
Trong ví dụ trên nhờ vào hàm type() mà chúng ta biết được thực chất tất cả các kiểu dữ liệu và các module mà chúng ta đã học thực chất đều là các đối tượng.
Trong ví dụ trên, các lớp mà chúng ta kiểm tra bằng hàm type là các lớp có sẵn trong Python. Khi chúng ta muốn định nghĩa một lớp cho riêng mình thì chúng ta dùng từ khóa class. Một lớp có thể hiểu là một bản thiết kế để tạo ra một thực thể (mà chúng ta gọi là đối tượng). Một đối tượng là một thực thể được xây dựng từ một bản thiết kế đó (mà chúng ta gọi là lớp). Ví dụ chúng ta định nghĩa lớp Dog, thì chúng ta có các đối tượng là Huck, Lulu…. 😀
class First:
pass
fr = First()
print (type(fr))
print (type(First))
Trong đoạn code trên chúng ta định nghĩa một lớp. Phần thân lớp tạm thời được để trống.
fr = First()
Ở dòng trên chúng ta xây dựng nên thực thể fr từ bản thiết kế First. Hay nói cách khác là chúng ta đã tạo ra đối tượng fr từ lớp First.
<class '__main__.First'>
<class 'type'>
Bên trong một lớp, chúng ta có thể định nghĩa các biến và hàm, biến và hàm bên trong một lớp được gọi là thuộc tính và phương thức. Chẳng hạn chúng ta định nghĩa lớp Employee (nhân viên), bên trong lớp này sẽ có thuộc tính Salary (lương) và phương thức calculateSalary (tính tiền lương).
Thuộc tính
Thuộc tính là biến nằm trong một lớp. Thuộc tính mô tả các đặc tính của một đối tượng. Trong Python có một phương thức đặc biệt gọi là __init__() dùng để khởi tạo giá trị cho các thuộc tính của một đối tượng.
class Cat:
def __init__(self, name):
self.name = name
missy = Cat('Missy')
lucky = Cat('Lucky')
print (missy.name)
print (lucky.name)
Trong đoạn code trên, chúng ta định nghĩa lớp Cat. Bên trong lớp này, chúng ta định nghĩa phương thức __init__(). Phương thức __init__() là phương thức khởi tạo của tất cả các lớp, mỗi khi tạo một đối tượng phương thức này sẽ tự động được gọi.
def __init__(self, name):
Bất cứ phương thức nào của Python cũng đều phải có tham số đầu tiên là self rồi mới đến các tham số khác.self thực ra chỉ là biến đối tượng đã gọi phương thức này mà thôi. Chẳng hạn trong ví dụ trên khi chúng ta gọi missy = Cat("Missy") thì self chính là missy.
self.name = name
Ở dòng trên chúng ta khởi tạo thuộc tính name và gán giá trị cho nó.
missy = Cat('Missy')
lucky = Cat('Lucky')
Trong hai dòng trên chúng ta dùng hàm Cat() để tạo ra hai đối tượng lớp thuộc lớp Cat. Chúng ta không trực tiếp đưa tham số self vào mà chỉ đưa các tham số thứ 2 trở đi thôi, self được tự động đưa vào bởi trình thông dịch. Trong ví dụ trên “Missy” và “Lucky” sẽ được gán cho thuộc tính name trong lớp Cat.
print (missy.name)
print (lucky.name)
Trong hai dòng code trên chúng ta in ra giá trị của thuộc tính name trong hai đối tượng Cat. Để truy xuất đến thuộc tính của một đối tượng thì chúng ta ghi tên đối tượng, dấu chấm và tên thuộc tính.
Missy
Lucky
Ngoài ra bạn có thể gán giá trị cho các thuộc tính ở bất cứ đâu sau phần định nghĩa lớp chứ không chỉ riêng bên trong phương thức khởi tạo.
class Dynamic:
pass
d = Dynamic()
d.name = "Dynamic"
print (d.name)
Trong ví dụ trên chúng ta định nghĩa lớp Dynamic.
d.name = "Dynamic"
Ở dòng này chúng ta khai báo và gán giá trị cho thuộc tính name.
Dynamic
Một đặc điểm nữa trong Python bạn có thể định nghĩa các thuộc tính chung cho mọi đối tượng.
class Cat:
species = 'mammal'
def __init__(self, name, age):
self.name = name
self.age = age
missy = Cat('Missy', 3)
lucky = Cat('Lucky', 5)
print (missy.name, missy.age)
print (lucky.name, lucky.age)
print (Cat.species)
print (missy.__class__.species)
print (lucky.species)
Trong ví dụ trên, chúng ta tạo ra hai đối tượng Cat với thuộc tính name và age, hai thuộc tính này được khai báo bên trong phương thức __init__() và được gọi là thuộc tính đối tượng. Ngoài ra hai đối tượng này còn có một thuộc tính chung là thuộc tính species,thuộc tính này được khai báo ở ngoài phương thức __init__() vàđược gọi là thuộc tính lớp. Thuộc tính lớp được chia sẻ chung cho mọi đối tượng của lớp đó, trong khi thuộc tính đối tượng chỉ dành riêng cho đối tượng. Nghĩa là khi bạn thay đổi giá trị của thuộc tính chia sẻ trong một đối tượng thì tất cả các thuộc tính chia sẻ đó trong các đối tượng khác cũng thay đổi theo.
Có hai cách để truy xuất thuộc tính lớp, thứ nhất là thông qua tên lớp, cách thứ hai là thông qua một thuộc tính đặc biệt nữa là thuộc tính __class__.
Missy 3
Lucky 5
mammal
mammal
mammal
Phương thức
Phương thức chẳng qua là các hàm, chỉ khác hàm bình thường ở chỗ chúng được định nghĩa bên trong một lớp. Các phương thức được sử dụng để thực hiện các công việc cụ thể. Phương thức là một thành phần quan trọng trong khái niệm Encapsulation (đóng gói). Chẳng hạn như bạn có một lớp tên là Network, lớp này có phương thức connect() dùng để kết nối đến các máy tính trong mạng LAN, thì bạn chỉ quan tâm rằng phương thức này làm công việc kết nối mạng chứ không quan tâm đến việc nó kết nối như thế nào.
Phương thức setRadius() thiết lập giá trị cho thuộc tính radius (bán kính).
def getRadius(self):
return self.radius
Phương thức getRadius() trả về lấy giá trị bán kính.
c.setRadius(5)
Chúng ta gọi các phương thức thông qua biến đối tượng.
5
78.5398
Kế thừa
Kế thừa là định nghĩa một lớp dựa trên một lớp đã được định nghĩa trước đó. Lớp kế thừa từ lớp khác được gọi là lớp dẫn xuất, lớp được các lớp khác kế thừa mình thì gọi là lớp cơ sở. Kế thừa trong lập trình hướng đối tượng cho phép chúng ta sử dụng lại mã nguồn và giảm độ phức tạp của chương trình. Lớp dẫn xuất có thể kế thừa hoặc mở rộng các tính năng của lớp cơ sở.
Trong ví dụ trên chúng ta định nghĩa hai lớp là là Animal và lớp Dog. Lớp Dog kế thừa từ lớp Animal, thừa hưởng một số phương thức của lớp Animal và có các phương thức của riêng nó. Ở trên lớp Dog kế thừa phương thức eat(), kế thừa và thay đổi phương thức whoAmI(), ngoài ra lớp Dog còn có phương thức của riêng nó là phương thức bark().
class Dog(Animal):
def __init__(self):
Animal.__init__(self)
print ("Dog created")
Để kế thừa một lớp thì chúng ta đặt tên lớp đó bên trong cặp dấu ngoặc tròn () ngay phía sau phần định nghĩa tên lớp. Nếu bên trong lớp cơ sở đã định nghĩa phương thức __init__(), chúng ta phải gọi lại phương thức __init__() từ lớp cơ sở.
Animal created
Dog created
Dog
Eating
Woof!
Đa hình
Đa hình là bạn có thể sử dụng các toán tử hay các hàm trên nhiều kiểu dữ liệu khác nhau.
a = "alfa"
b = (1, 2, 3, 4)
c = ['o', 'm', 'e', 'g', 'a']
print (a[2])
print (b[1])
print (c[3])
Bản thân các hàm có sẵn trong Python cũng có tính chất đa hình. Chẳng hạn như hàm print() mà chúng ta hay dùng, mỗi lần gọi hàm này bạn có thể đưa vào hầu hết các kiểu dữ liệu khác nhau.
f
2
g
Trong hướng đối tượng thì tính đa hình càng được phát huy tác dụng.
class Animal:
def __init__(self, name=''):
self.name = name
def talk(self):
pass
class Cat(Animal):
def talk(self):
print ("Meow!")
class Dog(Animal):
def talk(self):
print ("Woof!")
a = Animal()
a.talk()
c = Cat("Missy")
c.talk()
d = Dog("Rocky")
d.talk()
Trong ví dụ trên, chúng ta định nghĩa hai lớp chó (Dog) và mèo (Cat) kế thừa từ lớp Animal. Do đó cả hai lớp này đều kế thừa phương thức talk() của lớp Animal, nhưng mỗi lớp lại in ra hai dòng text khác nhau.
Meow!
Woof!
Các phương thức đặc biệt
Tất cả các lớp dù là có sẵn hay do chúng ta định nghĩa đều kế thừa từ một lớp gốc trong Python có tên là object. Lớp này có sẵn một số phương thức và đương nhiên là các lớp do chúng ta định nghĩa đều kế thừa các phương thức này, ví dụ như phương thức __init__()… Trong Python khi chúng ta gọi đến các hàm hay toán tử được xây dựng sẵn như print(), del… chúng sẽ gọi đến các phương thức gốc của lớp object. Chính vì các lớp do chúng ta định nghĩa đều được kế thừa từ lớp object nên chúng ta cũng có thể dùng các hàm hay toán tử có sẵn trong Python với các lớp của chúng ta. Trong các ngôn ngữ như C++ thì tính chất này được gọi là quá tải toán tử (operator overloading).
class Book:
def __init__(self, title, author, pages):
print ("A book is created")
self.title = title
self.author = author
self.pages = pages
def __str__(self):
return "Title:%s , author:%s, pages:%s " % \
(self.title, self.author, self.pages)
def __len__(self):
return self.pages
def __del__(self):
print ("A book is destroyed")
book = Book("Inside Steve's Brain", "Leander Kahney", 304)
print (book)
print (len(book))
del book
Trong ví dụ trên, chúng ta định nghĩa lớp Book. Bên trong lớp này chúng ta định nghĩa 4 phương thức được kế thừa từ lớp object là __init__(),__str__(), __len__() và __del__().
print (book)
Khi sử dụng hàm print(), hàm này sẽ gọi đến phương thức __str__(), do đó khi chúng ta kế thừa lại phương thức này, hàm print() sẽ gọi đến phương thức __str__() trong lớp Book của chúng ta.
print (len(book))
Tương tự, hàm len() gọi đến phương thức __len__()
del book
Từ khóa del gọi đến phương thức __del__() và trên thực tế là phương thức này làm công việc hủy bỏ một đối tượng ra khỏi bộ nhớ. Nhưng ở đây chúng ta kế thừa phương thức này chỉ làm công việc là in ra một đoạn text.
Trong ví dụ dưới đây, chúng ta sẽ kế thừa các toán tử cộng và trừ.
class Vector:
def __init__(self, data):
self.data = data
def __str__(self):
return repr(self.data)
def __add__(self, other):
data = []
for j in range(len(self.data)):
data.append(self.data[j] + other.data[j])
return Vector(data)
def __sub__(self, other):
data = []
for j in range(len(self.data)):
data.append(self.data[j] - other.data[j])
return Vector(data)
x = Vector([1, 2, 3])
y = Vector([3, 0, 2])
print (x + y)
print (y - x)
def __add__(self, other):
data = []
for j in range(len(self.data)):
data.append(self.data[j] + other.data[j])
return Vector(data)
Chúng ta định nghĩa lớp Vector, lớp vector này kế thừa hai phương thức __add__() và __sub__(). phương thức __add__() sẽ được gọi khi sử dụng toán tử + và phương thức __sub__() sẽ được gọi khi sử dụng toán tử -.