Category Archives: Python

Tkinter – Menu

Trong phần này chúng ta sẽ học cách tạo menu trong Tkinter.

Ví dụ

from tkinter import Frame, Tk, Menu

class Example(Frame):
  def __init__(self, parent):
    Frame.__init__(self, parent)
 
    self.parent = parent
    self.initUI()
  
  def initUI(self):
    self.parent.title("Simple Menu")
 
    menuBar = Menu(self.parent)
    self.parent.config(menu=menuBar)
 
    fileMenu = Menu(menuBar)
    fileMenu.add_command(label="Exit", command=self.onExit)
    menuBar.add_cascade(label="File", menu=fileMenu)
 
  def onExit(self):
    self.quit()
 
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()

Đoạn code trên tạo một cửa sổ có một menu, trong menu này có nút Exit, bấm vào nút này thì thoát chương trình.

menubar = Menu(self.parent)
self.parent.config(menu=menubar)

Để hiển thị các menu thì đầu tiên chúng ta phải tạo một menu bar trước. Chúng ta tạo menu bar từ lớp Menu.

fileMenu = Menu(menubar)

Tiếp theo chúng ta tạo menu File cũng từ lớp Menu.

fileMenu.add_command(label="Exit", command=self.onExit)

Để thêm các item vào một menu thì chúng ta dùng phương thức add_command(), ở trên chúng ta tạo item Exit và cho item này gọi đến phương thức onExit().

menubar.add_cascade(label="File", menu=fileMenu)

Sau khi tạo xong thì chúng ta gắn menu File vào menu bar bằng phương thức add_cascade().

Untitled

Tạo menu con

Ví dụ dưới đây sẽ tạo một menu con từ một menu cha.

Chúng ta tạo menu File, trong menu này có 1 item, 1 separator (đường kẻ ngang phân cách) và 1 menu con. Trong menu con có 3 item khác.

submenu = Menu(fileMenu)
submenu.add_command(label="New feed")
submenu.add_command(label="Bookmarks")
submenu.add_command(label="Mail")

Chúng ta tạo menu con cũng giống như tạo một menu bình thường từ lớp Menu.

fileMenu.add_cascade(label='Import', menu=submenu)

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().

Untitled

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ổ.

from Tkinter import Tk, Frame, Menu


class Example(Frame):
  
    def __init__(self, parent):
        Frame.__init__(self, parent)   
         
        self.parent = parent
        
        self.initUI()
        
        
    def initUI(self):
      
        self.parent.title("Popup menu")
        self.menu = Menu(self.parent, tearoff=0)
        self.menu.add_command(label="Beep", command=self.bell())
        self.menu.add_command(label="Exit", command=self.onExit)

        self.parent.bind("<Button-3>", self.showMenu)
        self.pack()
        
        
    def showMenu(self, e):
        self.menu.post(e.x_root, e.y_root)
       

    def onExit(self):
        self.quit()


root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()  

Trong ví dụ trên chúng tạo popup menu có 2 item khi click chuột phải lên cửa sổ.

self.menu = Menu(self.parent, tearoff=0)

Ban đầu chúng ta cũng tạo một đối tượng Menu như menu bình thường. Tham số tearoff=0 sẽ tắt chế độ hiển thị menu trên một cửa sổ riêng.

self.parent.bind("<Button-3>", self.showMenu)

Dòng code trên gắn sự kiện click chuột phải (<Button-3>) vào phương thức showMenu().

def showMenu(self, e):
    self.menu.post(e.x_root, e.y_root)

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ố là đối tượng của sự kiện click chuột.

Untitled

Tkinter – Widget

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.

Checkbutton

Checkbutton là widget hiển thị hộp đánh dấu.

from tkinter import Tk, Frame, Checkbutton
from tkinter import BooleanVar, BOTH

class Example(Frame):
  def __init__(self, parent):
    Frame.__init__(self, parent)
 
    self.parent = parent
    self.initUI()
 
  def initUI(self):
    self.parent.title("Checkbutton")
    self.pack(fill=BOTH, expand=True)
    self.var = BooleanVar()
    
    cb = Checkbutton(self, text="Show Title", variable=self.var, command=self.onClick)
    cb.select()
    cb.place(x=50, y=50)
 
  def onClick(self):
    if self.var.get() == True:
      self.master.title("Checkbutton")
    else:
      self.master.title("")
 
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()

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.

cb = Checkbutton(self, text="Show title", variable=self.var, command=self.onClick)

Ở 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.

Capture

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.

from PIL import Image, ImageTk
from tkinter import Tk, Frame, Label

class Example(Frame):
  def __init__(self, parent):
    Frame.__init__(self, parent)
 
    self.parent = parent
 
    self.initUI()
 
  def initUI(self):
    self.parent.title("Label")
 
    self.img = Image.open("C:\\tatras.jpg")
    tatras = ImageTk.PhotoImage(self.img)
    label = Label(self, image=tatras)
 
    label.image = tatras
 
    label.pack()
    self.pack()
 
  def setGeometry(self):
    w, h = self.img.size
    self.parent.geometry(("%dx%d+300+300") % (w, h))
 
root = Tk()
ex = Example(root)
ex.setGeometry()
root.mainloop()

Để chạy được ví dụ trên thì bạn phải có thư viện Pillow trong Python.

from PIL import Image, ImageTk

Mặc định thì Label trong Tkinter cũng có các module có thể hiển thị ảnh nhưng rất hạn chế.

label = Label(self, image=tatras)

Chúng ta dùng tham số image khi khởi tạo để gán ảnh vào label.

label.image = tatras

Tiếp theo bạn phải tạo một biến để giữ lại tham chiếu đến ảnh nếu không bộ thu dọn tài nguyên của Python sẽ xóa mất ảnh của chúng ta.

w, h = self.img.size
self.parent.geometry(("%dx%d+300+300") % (w, h))

Hai dòng code trên chúng ta cho kích thước màn hình bằng đúng với kích thước ảnh.

Capture

Scale

Scale là widget hiển thị một thanh cuộn gắn với một khoảng giá trị nào đó.

from tkinter import Tk, BOTH, IntVar, LEFT
from tkinter.ttk import Frame, Label, Scale, Style

class Example(Frame):
  def __init__(self, parent):
    Frame.__init__(self, parent)
    self.parent = parent
    self.initUI()
 
  def initUI(self):
    self.parent.title("Scale")
    self.style = Style()
    self.style.theme_use("default")
 
    self.pack(fill=BOTH, expand=1)
 
    scale = Scale(self, from_=0, to=100, command=self.onScale)
    scale.pack(side=LEFT, padx=15)
 
    self.var = IntVar()
    self.label = Label(self, text=0, textvariable=self.var)
    self.label.pack(side=LEFT)
 
  def onScale(self, val):
    v = int(float(val))
    self.var.set(v)
 
root = Tk()
ex = Example(root)
root.geometry("250x100+300+300")
root.mainloop()

Trong ví dụ trên chúng ta cho hiển thị một scale và một label, text trên label sẽ thay đổi khi kéo thanh cuộn trên scale.

scale = Scale(self, from_=0, to=100, command=self.onScale)

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.

self.var = IntVar()
self.label = Label(self, text=0, textvariable=self.var)    

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

Capture

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.

Capture

Tkinter – Quản lý Layout

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à packgrid 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.

from PIL import Image, ImageTk
from tkinter import Tk, Label, BOTH
from tkinter.ttk import Frame, Style

class Example(Frame):
   def __init__(self, parent):
     Frame.__init__(self, parent)
 
     self.parent = parent
     self.initUI()
 
   def initUI(self):
     self.parent.title("Absolute positioning")
     self.pack(fill=BOTH, expand=1)
 
     Style().configure("TFrame", background="#333")
 
     bard = Image.open("C:\\bardejov.jpg")
     bardejov = ImageTk.PhotoImage(bard)
     label1 = Label(self, image=bardejov)
     label1.image = bardejov
     label1.place(x=20, y=20)
  
     rot = Image.open("C:\\rotunda.jpg")
     rotunda = ImageTk.PhotoImage(rot)
     label2 = Label(self, image=rotunda)
     label2.image = rotunda
     label2.place(x=40, y=160)
 
     minc = Image.open("C:\\mincol.jpg")
     mincol = ImageTk.PhotoImage(minc)
     label3 = Label(self, image=mincol)
     label3.image = mincol
     label3.place(x=170, y=50)
 
root = Tk()
root.geometry("300x280+300+300")
app = Example(root)
root.mainloop()

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 ImageImageTk 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.

Style().configure("TFrame", background="#333") 

Dòng trên sẽ tô màu nền cửa sổ là màu xám đen.

bard = Image.open("C:\\bardejov.jpg")
bardejov = ImageTk.PhotoImage(bard)

Hai dòng trên sẽ tạo các đối tượng ảnh và tải chúng vào chương trình.

label1 = Label(self, image=bardejov)

Chúng ta tạo đối tượng Label, đây là một lớp dùng để hiển thị chữ và ảnh.

label1.image = bardejov

Chúng ta phải lưu lại tham chiếu đến ảnh nếu không sẽ bị xóa.

label1.place(x=20, y=20)

Chúng ta đặt ảnh tại vị trí x = 20 và y = 20.

Capture

Button

Trong ví dụ dưới đây, chúng ta sẽ hiển thị hai button (nút bấm) ở góc trái màn hình sử dụng layout pack.

from tkinter import Tk, RIGHT, BOTH, RAISED
from tkinter.ttk import Frame, Button, Style

class Example(Frame):
   def __init__(self, parent):
     Frame.__init__(self, parent)
     self.parent = parent
     self.initUI()
 
   def initUI(self):
     self.parent.title("Button")
     self.style = Style()
     self.style.theme_use("default")
 
     frame = Frame(self, relief=RAISED, borderwidth=1)
     frame.pack(fill=BOTH, expand=True)
     self.pack(fill=BOTH, expand=True)
     
     closeButton = Button(self, text="Close")
     closeButton.pack(side=RIGHT, padx=5, pady=5)
     okButton = Button(self, text="OK")
     okButton.pack(side=RIGHT)
 

root = Tk()
root.geometry("300x200+300+300")
app = Example(root)
root.mainloop()

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ổ.

frame = Frame(self, relief=RAISED, borderwidth=1)
frame.pack(fill=BOTH, expand=True)

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.

closeButton = Button(self, text="Close")
closeButton.pack(side=RIGHT, padx=5, pady=5)

Ở 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, padxpady 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.

Capture

Tạo Form

Trong ví dụ dưới đây, chúng ta tiếp tục tìm hiểu về layout pack. 

from tkinter import Tk, Text, TOP, BOTH, X, N, LEFT
from tkinter.ttk import Frame, Label, Entry

class Example(Frame):
   def __init__(self, parent):
     Frame.__init__(self, parent)
 
     self.parent = parent
     self.initUI()
 
   def initUI(self):
     self.parent.title("Review")
     self.pack(fill=BOTH, expand=True)
 
     frame1 = Frame(self)
     frame1.pack(fill=X)
 
     lbl1 = Label(frame1, text="Title", width=6)
     lbl1.pack(side=LEFT, padx=5, pady=5)
 
     entry1 = Entry(frame1)
     entry1.pack(fill=X, padx=5, expand=True)
   
     frame2 = Frame(self)
     frame2.pack(fill=X)
 
     lbl2 = Label(frame2, text="Author", width=6)
     lbl2.pack(side=LEFT, padx=5, pady=5)
 
     entry2 = Entry(frame2)
     entry2.pack(fill=X, padx=5, expand=True)
 
     frame3 = Frame(self)
     frame3.pack(fill=BOTH, expand=True)
 
     lbl3 = Label(frame3, text="Review", width=6)
     lbl3.pack(side=LEFT, anchor=N, padx=5, pady=5)
 
     txt = Text(frame3)
     txt.pack(fill=BOTH, pady=5, padx=5, expand=True)
 
root = Tk()
root.geometry("300x300+300+300")
app = Example(root)
root.mainloop() 

Chúng ta thiết kế một cửa sổ hiển thị form đánh giá.

self.pack(fill=BOTH, expand=True)

Đầu tiên chúng ta tạo frame chính để chứa các widget còn lại kể cả các frame khác.

frame1 = Frame(self)
frame1.pack(fill=X)

lbl1 = Label(frame1, text="Title", width=6)
lbl1.pack(side=LEFT, padx=5, pady=5)           

entry1 = Entry(frame1)
entry1.pack(fill=X, padx=5, expand=True)

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ự.

frame3 = Frame(self)
frame3.pack(fill=BOTH, expand=True)

lbl3 = Label(frame3, text="Review", width=6)
lbl3.pack(side=LEFT, anchor=N, padx=5, pady=5)        

txt = Text(frame3)
txt.pack(fill=BOTH, pady=5, padx=5, expand=True) 

Trong frame thứ ba chúng ta đặt một Label và một Text. Label được đặt về phía trên của frame, còn text tự động co dãn theo kích thước cửa sổ.

Capture

Grid layout

Tiếp theo chúng ta sẽ tìm hiểu về layout grid.

from tkinter import Tk, W, E
from tkinter.ttk import Frame, Button, Style
from tkinter.ttk import Entry

class Example(Frame):
 
 def __init__(self, parent):
   Frame.__init__(self, parent) 
 
   self.parent = parent
   self.initUI()

 
 def initUI(self):
 
   self.parent.title("Calculator")
 
   Style().configure("TButton", padding=(0, 5, 0, 5), font='serif 10')
 
   self.columnconfigure(0, pad=3)
   self.columnconfigure(1, pad=3)
   self.columnconfigure(2, pad=3)
   self.columnconfigure(3, pad=3)
 
   self.rowconfigure(0, pad=3)
   self.rowconfigure(1, pad=3)
   self.rowconfigure(2, pad=3)
   self.rowconfigure(3, pad=3)
   self.rowconfigure(4, pad=3)
 
   entry = Entry(self)
   entry.grid(row=0, columnspan=4, sticky=W+E)
   cls = Button(self, text="Cls")
   cls.grid(row=1, column=0)
   bck = Button(self, text="Back")
   bck.grid(row=1, column=1)
   lbl = Button(self)
   lbl.grid(row=1, column=2) 
   clo = Button(self, text="Close")
   clo.grid(row=1, column=3) 
   sev = Button(self, text="7")
   sev.grid(row=2, column=0) 
   eig = Button(self, text="8")
   eig.grid(row=2, column=1) 
   nin = Button(self, text="9")
   nin.grid(row=2, column=2) 
   div = Button(self, text="/")
   div.grid(row=2, column=3) 
  
   fou = Button(self, text="4")
   fou.grid(row=3, column=0) 
   fiv = Button(self, text="5")
   fiv.grid(row=3, column=1) 
   six = Button(self, text="6")
   six.grid(row=3, column=2) 
   mul = Button(self, text="*")
   mul.grid(row=3, column=3) 
 
   one = Button(self, text="1")
   one.grid(row=4, column=0) 
   two = Button(self, text="2")
   two.grid(row=4, column=1) 
   thr = Button(self, text="3")
   thr.grid(row=4, column=2) 
   mns = Button(self, text="-")
   mns.grid(row=4, column=3) 
 
   zer = Button(self, text="0")
   zer.grid(row=5, column=0) 
   dot = Button(self, text=".")
   dot.grid(row=5, column=1) 
   equ = Button(self, text="=")
   equ.grid(row=5, column=2) 
   pls = Button(self, text="+")
   pls.grid(row=5, column=3)
 
   self.pack()

root = Tk()
app = Example(root)
root.mainloop()

Đoạn code trên thiết kế giao diện của một chiếc máy tính bỏ túi.

Style().configure("TButton", padding=(0, 5, 0, 5), font='serif 10')

Chúng ta quy định kiểu phông chữ thông qua tham số font và khoảng cách của frame với cửa sổ thông qua tham số padding.

self.columnconfigure(0, pad=3)
...        
self.rowconfigure(0, pad=3)

Hai phương thức columnconfigure() và rowconfigure() quy định khoảng cách giữa các widget.

entry = Entry(self)
entry.grid(row=0, columnspan=4, sticky=W+E)

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.

cls = Button(self, text="Cls")
cls.grid(row=1, column=0)

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.

Capture

Tkinter – Giới thiệu về Tkinter

 

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 logo

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.

Trang chủ của Python nằm tại địa chỉ python.org.

Ví dụ

Đoạn code dưới đây ví dụ về cách tạo một cửa sổ bằng Tkinter.

from tkinter import Tk, Frame, BOTH

class Example(Frame):
 def __init__(self, parent):
 Frame.__init__(self, parent, background="white")
 self.parent = parent
 self.initUI()
 
 def initUI(self):
 self.parent.title("Simple")
 self.pack(fill=BOTH, expand=1)
 
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()

Đ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 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 Excample kế 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ý.
Capture

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.

from tkinter import Tk, BOTH
from tkinter.ttk import Frame, Button, Style

class Example(Frame):
 def __init__(self, parent):
 Frame.__init__(self, parent)
 
 self.parent = parent
 self.initUI()
 
 def initUI(self):
 self.parent.title("Quit button")
 self.style = Style()
 self.style.theme_use("default")
 
 self.pack(fill=BOTH, expand=1)
 
 quitButton = Button(self, text="Quit", command=self.quit)
 quitButton.place(x=50, y=50)
 
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()

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.

self.style = Style()
self.style.theme_use("default")

Để 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.

quitButton = Button(self, text="Quit",
    command=self.quit)

Ở 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ổ.

Capture

Python – Các hàm kiểm tra trạng thái

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 Python mọ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.

>>> dir(())
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']

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.

import sys

class Object:
   def __init__(self):
      pass
   def examine(self):
      print (self)


o = Object()

print (dir(o))
print (dir([]))
print (dir({}))
print (dir(1))
print (dir())
print (dir(len))
print (dir(sys))
print (dir("String"))

Trong đoạn code trên chúng ta kiểm tra một vài đối tượng khác nhau trong đó có cả đối tượng thuộc lớp do chúng ta định nghĩa.

Nếu không đưa tham số vào thì hàm dir() trả về tên các module trong chương trình.

>>> dir()
['__builtins__', '__doc__', '__name__', '__package__']
>>> import sys
>>> import math, os
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'math', 'os', 'sys']

Hàm type() và id()

Hàm type() trả về kiểu dữ liệu của một đối tượng.

import sys

def function(): pass

class MyObject():
   def __init__(self):
      pass

o = MyObject()

print (type(1))
print (type(""))
print (type([]))
print (type({}))
print (type(()))
print (type(object))
print (type(function))
print (type(MyObject))
print (type(o))
print (type(sys))

Trong ví dụ trên chúng ta in ra kiểu dữ liệu của một số đối tượng.

<type 'int'>
<type 'str'>
<type 'list'>
<type 'dict'>
<type 'tuple'>
<type 'type'>
<type 'function'>
<type 'classobj'>
<type 'instance'>
<type 'module'>

Hàm id() sẽ trả về id của một đối tượng.

import sys

def fun(): pass

class MyObject():
   def __init__(self):
      pass

o = MyObject()

print (id(1))
print (id(""))
print (id({}))
print (id([]))
print (id(sys))
print (id(fun))
print (id(MyObject))
print (id(o))
print (id(object))

Trong ví dụ trên chúng ta in ra id của một số đối tượng, kể cả đối tượng có sẵn trong Python.

135717024
3084304536
3084111220
3084104940
3084304500
3084112812
3084074556
3084130444
135568640

Module sys

Module sys cung cấp các thông tin về hệ điều hành và tình trạng của Python.

>>> import sys
>>> sys.version
'3.5.1 (v3.5.1:37a07cee5969, Dec  6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)]'
>>> sys.platform
'win32'
>>> sys.path
['C:\\Users\\PhoCode\\', 'C:\\python\\python35.zip', 'C:\\python\\DLLs', 'C:\\python\\lib', 'C:\\python', 'C:\\python\\lib\\site-packages']

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.

>>> sys.maxsize
2147483647
>>> sys.executable
'C:\\python\\python.exe'
>>> sys.argv
['']

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.

Một số hàm khác

def fun(): pass

print (hasattr(object, '__doc__'))
print (hasattr(fun, '__doc__'))
print (hasattr(fun, '__call__'))

print (getattr(object, '__doc__'))
print (getattr(fun, '__doc__'))

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ó.

class Object():
   def __init__(self):
      pass

class Wall(Object):
   def __init__(self):
      pass

print (issubclass(Object, Object))
print (issubclass(Object, Wall))
print (issubclass(Wall, Object))
print (issubclass(Wall, Wall))

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____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.

True
True
False
False

 

Python – Iterator và Generator

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__()__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.

str = "formidable"
it = iter(str)

print (it.__next__())
print (it.__next__())
print (it.__next__())

print (list(it))

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.

def __next__(self):
    self.x += 1
    return self.x**self.x

Phương thức __next__() trả về phần tử tiếp theo trong dãy.

 def __iter__(self):
    return self

Phương thức __iter__ trả về đối tượng tham chiếu đến chính nó (hay còn gọi là đối tượng iterator).

 if n > 10:
      break

Do chúng ta tạo một danh sách dài vô tận nên phải có câu lệnh để ngắt vòng lặp.

1
4
27
256
3125
46656
823543
16777216
387420489
10000000000
285311670611

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.

(1, 2)
(2, 2)
Iteration finished

 

Python – Exception

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.

except ValueError:
   pass
except (IOError, OSError):
   pass

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).

try:
    3/0
except ZeroDivisionError, e:
    print ("Cannot divide by zero")
    print ("Message:", e.message)
    print ("Class:", e.__class__)

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.

Python – Package

Trong bài này chúng ta sẽ tìm hiểu về gói (Package) trong Python.

Một package đơn giản là một thư mục chứa các module có liên quan với nhau, trong thư mục này có một file đặc biệt tên là __init__.py.

Như trong bài trước chúng ta đã biết trong Python có các cách sau để quản lý code:

  • Dùng hàm
  • Dùng lớp
  • Dùng module
  • Dùng package

Nếu chương trình của chúng ta đủ lớn (cỡ hàng trăm module) thì chúng ta nên dùng package.

Để tạo một package thì bạn làm những bước sau đây:

  • B1: Tạo một thư mục với tên package mà bạn muốn đặt
  • B2: Trong thư mục đó tạo một file có tên __init__.py
  • B3: Đặt các module mà bạn viết vào thư mục đó

Bất cứ thư mục nào chứa file __init__.py sẽ sẽ trở thành một package của Python.

Ví dụ: mình tạo một folder với tên constantstrong file này mình tạo file __init__.py và một module có tên names (file có tên names.py)

test_package.py
constants/
          __init__.py
          names.py 
print ("Package names initialized!")
names = ('Jack', 'Jessica', 'Robert', 'Lucy', 'Tom')

Vậy là bạn đã tạo xong package constants rồi. Bây giờ chúng ta sẽ tạo một file script Python ở ngoài để dùng thử.

from constants import names
print (names.names)

Để sử dụng package thì bạn dùng câu lệnh import như dùng với module bình thường.

Package names initialized!
('Jack', 'Jessica', 'Robert', 'Lucy', 'Tom')

Bạn cũng có thể tạo một package trong một package hay còn gọi là các package con.

test_package.py
constants/
          __init__.py
          names.py 
          numbers/
                   __init__.py
                   integers.py

Trong ví dụ trên chúng tạo package numbers trong package constants. Bên trong package numbers có một module tên là integers.

print ("Package constants.numbers initialized")

Các file __init__.py bạn có thể để trống chứ không cần ghi gì trong đó cả.

integers = (2, 3, 45, 6, 7, 8, 9)

from constants import names
from constants.numbers import integers

print (names.names)
print (integers.integers)

Để import package con thì bạn dùng dấu chấm.

Package constants initialized!
Package constants.numbers initialized!
('Jack', 'Jessica', 'Robert', 'Lucy', 'Tom')
(2, 3, 45, 6, 7, 8, 9)

 

Python – Module

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. 

"""
An empty module
"""

import empty
import sys

print (__name__)
print (empty.__name__)
print (sys.__name__)

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.


import sys
import textwrap

sp = sorted(sys.path)
dnames = ', '.join(sp)

print (textwrap.fill(dnames))

Đoạn code trên in ra tất cả các thư mục trong biến sys.path.

import textwrap

Module textwrap dùng để định dạng kiểu hiển thị.

sp = sorted(sys.path)

Dòng code trên lấy danh sách các đường dẫn thư mục và sắp xếp chúng.

dnames = ', '.join(sp)

Sau đó chúng ta nối chúng lại thành một chuỗi, ngăn cách bởi dấu phẩy.

C:\Users\PhoCode\, C:\Python, C:\Python\DLLs, C:\Python\lib,
C:\Python\lib\site-packages, C:\Python\python35.zip

Từ khóa import

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.


try:
    import empty2
except ImportError, e:
    print ('Failed to import:', e)

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-1i-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.

>>> import fibonacci as fib
>>> fib.fib(500)
1 1 2 3 5 8 13 21 34 55 89 144 233 377

Python – Hướng đối tượng trong Python

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.


import sys


def function(): pass


print (type(1))
print (type(""))
print (type([]))
print (type({}))
print (type(()))
print (type(object))
print (type(function))
print (type(sys))

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.

<class 'int'>
<class 'str'>
<class 'list'>
<class 'dict'>
<class 'tuple'>
<class 'type'>
<class 'function'>
<class 'module'>

Từ khóa class

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ínhphươ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__()  đượ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.

print (Cat.species)
print (missy.__class__.species)

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.

class Circle:
   pi = 3.141592

   def __init__(self, radius=1):
      self.radius = radius 

   def area(self):
      return self.radius * self.radius * Circle.pi

   def setRadius(self, radius):
      self.radius = radius

   def getRadius(self):
      return self.radius


c = Circle()

c.setRadius(5)
print (c.getRadius())
print (c.area())

Trong ví dụ trên, chúng ta định nghĩa lớp Circle (hình tròn) và 3 phương thức mới.

 def area(self):
    return self.radius * self.radius * Circle.pi

Phương thức area() trả về diện tích hình tròn.

def setRadius(self, radius):
   self.radius = radius

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ở.

class Animal:
   def __init__(self):
      print ("Animal created")

   def whoAmI(self):
      print ("Animal")

   def eat(self):
      print ("Eating")


class Dog(Animal):
   def __init__(self):
      Animal.__init__(self)
      print ("Dog created")

   def whoAmI(self):
      print ("Dog")

   def bark(self):
      print ("Woof!")


d = Dog()
d.whoAmI()
d.eat()
d.bark()

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__()__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ử -.

[4, 2, 5]
[2, -2, -1]