Category Archives: Python – Lập trình Python

Python – Cơ sở dữ liệu SQLite

Trong phần này chúng ta sẽ làm việc với cơ sở dữ liệu SQLite.

Python cung cấp sẵn module sqlite3 hỗ trợ kết nối và thao tác với CSDL SQLite.

C:\User\PhoCode>python
Python 3.5.1 (v3.5.1:37a07cee5969, Dec 6 2015, 01:38:48) [MSC v.1900 32 bit (Intel)] 
on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> sqlite3.version
'2.6.0'
>>> sqlite3.sqlite_version
'3.8.11'

Chúng ta có thể kiểm tra một số thông tin về module sqlite3.

Trước khi bắt đầu chúng ta cần có một CSDL để test.

C:\User\PhoCode>sqlite3 test.db
SQLite version 3.7.13 2012-06-11 02:05:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"

Chúng ta tạo CSDL test.db.

Xem phiên bản SQLite

Trong ví dụ dưới đây chúng ta xem phiên bản CSDL SQLite.

import sqlite3 as lite
import sys
import os
con = None

try:
    path = os.path.dirname(__file__) + "\\test.db"
    con = lite.connect(path)
    
    cur = con.cursor()    
    cur.execute('SELECT SQLITE_VERSION()')
    
    data = cur.fetchone()
    
    print ("SQLite version: %s" % data)         
    
except lite.Error as e:
    
    print ("Error %s:" % e.args[0])
    sys.exit(1)
    
finally:
    
    if con:
        con.close()

Chúng ta kết nối với CSDL test.db và thực thi câu truy vấn lấy thông tin về phiên bản SQLite đang sử dụng.

import sqlite3 as lite

Đầu tiên chúng ta import module sqlite3.

con = None

Biến con là biến lưu trữ đối tượng Connection khi chúng ta kết nối CSDL.

path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path)

Để kết nối đến CSDL thì chúng ta dùng phương thức connect(), phương thức này trả về một đối tượng Connection.

cur = con.cursor()    
cur.execute('SELECT SQLITE_VERSION()')

Sau khi đã có đối tượng Connection, chúng ta lấy một đối tượng Cursor, đối tượng này làm nhiệm vụ duyệt qua các bản ghi trong tập dữ liệu được lấy về và thực thi các câu truy vấn. Để thực thi một câu truy vấn thì chúng ta dùng phương thức execute().

data = cur.fetchone()

Phương thức fetchone() lấy về dòng đầu tiên của bảng dữ liệu trả về.

print ("SQLite version: %s" % data)

Chúng ta in dòng dữ liệu đó ra màn hình.

finally:
    
    if con:
        con.close() 

Sau khi đã hoàn tất công việc thì chúng ta đóng kết nối tới CSDL với phương thức close().

SQLite version: 3.8.11

Trong ví dụ dưới đây, chúng ta cũng lấy phiên bản SQLite nhưng sử dụng từ khóa with.

import sqlite3 as lite
import sys
import os

path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path)

with con:
    
    cur = con.cursor()    
    cur.execute('SELECT SQLITE_VERSION()')
    
    data = cur.fetchone()
    
    print ("SQLite version: %s" % data)

Dùng từ khóa with có tác dụng làm cho code dễ chịu hơn và các câu lệnh SQL có liên quan đến việc cập nhật dữ liệu như INSERT, UPDATE, DELETE… sẽ tự động được thực thi (nếu không thì bạn phải gọi thêm phương thức commit() thì mới thực sự thực thi câu truy vấn lên CSDL).

with con:

Python sẽ tự động xử lý exception và tự động ngắt kết nối CSDL khi không dùng nữa nếu có từ khóa with.

INSERT

Chúng ta sẽ thực thi câu truy vấn INSERT.

import sqlite3 as lite
import sys
import os
path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path)

with con:
    
    cur = con.cursor()    
    cur.execute("CREATE TABLE Cars(Id INT, Name TEXT, Price INT)")
    cur.execute("INSERT INTO Cars VALUES(1,'Audi',52642)")
    cur.execute("INSERT INTO Cars VALUES(2,'Mercedes',57127)")
    cur.execute("INSERT INTO Cars VALUES(3,'Skoda',9000)")
    cur.execute("INSERT INTO Cars VALUES(4,'Volvo',29000)")
    cur.execute("INSERT INTO Cars VALUES(5,'Bentley',350000)")
    cur.execute("INSERT INTO Cars VALUES(6,'Citroen',21000)")
    cur.execute("INSERT INTO Cars VALUES(7,'Hummer',41400)")
    cur.execute("INSERT INTO Cars VALUES(8,'Volkswagen',21600)")

Đoạn code trên tạo bảng Cars và insert 8 dòng dữ liệu vào bảng này.

cur.execute("CREATE TABLE Cars(Id INT, Name TEXT, Price INT)")

Bảng Cars sẽ có 3 cột là Id, NamePrice.

cur.execute("INSERT INTO Cars VALUES(1,'Audi',52642)")
cur.execute("INSERT INTO Cars VALUES(2,'Mercedes',57127)")

Chúng ta chỉ cần dùng phương thức execute() để thực thi các câu lệnh SQL. Khi dùng từ khóa with thì các câu lệnh này sẽ được thực thi ngay trên CSDL.

sqlite> .mode column  
sqlite> .headers on
sqlite> SELECT * FROM Cars;
Id          Name        Price     
----------  ----------  ----------
1           Audi        52642     
2           Mercedes    57127     
3           Skoda       9000      
4           Volvo       29000     
5           Bentley     350000    
6           Citroen     21000     
7           Hummer      41400     
8           Volkswagen  21600 

Các câu lệnh như UPDATE, DELETE… bạn cũng làm tương tự.

Phương thức executemany()

Phương thức executemany() tiện lợi hơn bằng cách thực thi nhiều câu lệnh cùng một lúc.

import sqlite3 as lite
import sys
import os
cars = (
    (1, 'Audi', 52642),
    (2, 'Mercedes', 57127),
    (3, 'Skoda', 9000),
    (4, 'Volvo', 29000),
    (5, 'Bentley', 350000),
    (6, 'Hummer', 41400),
    (7, 'Volkswagen', 21600)
)


path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path)

with con:
    
    cur = con.cursor()    
    
    cur.execute("DROP TABLE IF EXISTS Cars")
    cur.execute("CREATE TABLE Cars(Id INT, Name TEXT, Price INT)")
    cur.executemany("INSERT INTO Cars VALUES(?, ?, ?)", cars)

Chúng ta xóa bảng và tạo lại bảng Cars.

cur.execute("DROP TABLE IF EXISTS Cars")
cur.execute("CREATE TABLE Cars(Id INT, Name TEXT, Price INT)")

Đầu tiên chúng ta kiểm tra xem bảng Cars đã tồn tại chưa, nếu rồi thì xóa bảng đó và tạo lại bảng mới.

cur.executemany("INSERT INTO Cars VALUES(?, ?, ?)", cars)

Chúng ta insert 8 dòng dữ liệu vào bảng bằng một phương thức duy nhất là executemany(), tham số đầu tiên là một câu lệnh SQL có tham số là các dấu ?, tham số thứ 2 là một tuple chứa nhiều tuple khác là các dữ liệu cần truyền vào.

SELECT

import sqlite3 as lite
import sys
import os

path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path)

with con:    
    
    cur = con.cursor()    
    cur.execute("SELECT * FROM Cars")

    rows = cur.fetchall()

    for row in rows:
        print (row)

Chúng ta sẽ lấy các bản ghi từ bảng Cars.

cur.execute("SELECT * FROM Cars")

Việc này cũng rất đơn giản, chỉ cần dùng phương thức execute() với câu SQL tương ứng.

rows = cur.fetchall()

Phương thức fetchall() sẽ trả về một tuple chứa các tuple là các dòng dữ liệu trong bảng.

for row in rows:
    print (row)

Chúng ta in các tuple đó ra màn hình.

(1, u'Audi', 52642)
(2, u'Mercedes', 57127)
(3, u'Skoda', 9000)
(4, u'Volvo', 29000)
(5, u'Bentley', 350000)
(6, u'Citroen', 21000)
(7, u'Hummer', 41400)
(8, u'Volkswagen', 21600)

Bạn cũng có thể in từng dòng một nếu muốn.

import sqlite3 as lite
import sys
import os

path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path)

with con:
    
    cur = con.cursor()    
    cur.execute("SELECT * FROM Cars")

    while True:
      
        row = cur.fetchone()
        
        if row == None:
            break
            
        print (row[0], row[1], row[2])

Chúng ta lấy từng dòng và in chúng ra màn hình.

while True:

Chúng ta dùng một vòng lặp để lặp qua từng dòng dữ liệu trong bảng. Vòng lặp kết thúc khi đối tượng Cursor đã đọc hết dữ liệu trong bảng.

row = cur.fetchone()

if row == None:
    break

Đối tượng Cursor có chứa một con trỏ chỉ đến các dòng trong bảng. Phương thức fetchone() sẽ đẩy con trỏ này lên một dòng và trả về dữ liệu của dòng đó, nếu con trỏ chỉ qua dòng cuối cùng thì sẽ trả về một đối tượng None.

print (row[0], row[1], row[2])

Dữ liệu trả về là một tuple nên bạn có thể truy xuất từng phần tử trong tuple bằng cặp dấu [].

1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600

Lấy phần tử thông qua tên cột

Như các ví dụ trên, dữ liệu trả về là một tuple chứa các tuple, nhưng bạn có thể quy định dữ liệu trả về dạng Dictionary, bằng cách đó bạn có thể truy cập vào các cột thông qua tên cột chứ không cần dùng chỉ số nữa.

import sqlite3 as lite
import os

path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path)   

with con:
    
    con.row_factory = lite.Row
       
    cur = con.cursor() 
    cur.execute("SELECT * FROM Cars")

    rows = cur.fetchall()

    for row in rows:
        print ("%s %s %s" % (row["Id"], row["Name"], row["Price"]))

Trong ví dụ này chúng ta sẽ lấy dữ liệu về dạng Dictionary.

con.row_factory = lite.Row

Để dữ liệu trả về là Dictionary thì chúng ta thiết lập thuộc tính row_factory trong đối tượng Connectionsqlite3.Row.

for row in rows:
    print ("%s %s %s" % (row["Id"], row["Name"], row["Price"]))

Dữ liệu trả về kiểu Dictionary và bạn có thể truy xuất dữ liệu của các ô thông qua tên cột.

Truyền tham số vào câu truy vấn

Truyền tham số vào câu truy vấn giúp tăng tốc độ thực thi câu truy vấn và đảm bảo an toàn cho ứng dụng khỏi kiểu tấn công SQL Injection.

import sqlite3 as lite
import sys
import os
uId = 1
uPrice = 62300 

path = os.path.dirname(__file__) + "\\test.db"
con = lite.connect(path) 

with con:

    cur = con.cursor()    

    cur.execute("UPDATE Cars SET Price=? WHERE Id=?", (uPrice, uId))        
    con.commit()
    
    print ("Number of rows updated: %d" % cur.rowcount)

Chúng ta thực thi câu lệnh UPDATE và dùng tham số trong câu lệnh SQL.

cur.execute("UPDATE Cars SET Price=? WHERE Id=?", (uPrice, uId)) 

Các tham số trong câu truy vấn được đại diện bằng dấu ?.

print ("Number of rows updated: %d" % cur.rowcount)

Ngoài ra trong đối tượng Cursor có thuộc tính rowcount chứa số lượng các dòng dữ liệu vừa được cập nhật.

Number of rows updated: 1

Python – Lập trình mạng với Socket

Python cung cấp module Socket hỗ trợ thực thi các giao thức mạng, dùng tạo các ứng dụng server hoặc client.

Chúng ta sẽ viết một ứng dụng Echo Server đơn giản.

Server

import socket

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print ("Starting server on port 10000")
server.bind((socket.gethostname(), 10000))
server.listen(1)

while True:
    conn, client = server.accept()
    try:
       print ("Connection from", client)
 
    while True:
        data = conn.recv(1024)
        print ("Receive from client:", data)
        if data:
            print ("Response to client")
            conn.sendall(data)
        else:
            print ("No data received")
            break
    finally:
        conn.close()

Server sẽ lắng nghe nghe các kết nối từ client, nhận tin nhắn từ client nếu có và gửi trả lời về lại client.

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Đầu tiên chúng ta tạo một đối tượng socket, tham số AF_INET cho biết chúng ta sử dụng IP v4, SOCK_TREAM là dùng giao thức TCP. Ngoài ra còn một số giá trị khác như AF_INET6 là dùng IP v6, AF_UNIX là chỉ kết nối các ứng dụng trong một máy (không dùng mạng), SOCK_DGRAM là dùng giao thức UDP.

print("Starting server on port 10000")
server.bind((socket.gethostname(), 10000))

Phương thức bind() chỉ định socket sẽ lắng nghe với địa chỉ IP của máy lấy từ phương thức gethostname() trên cổng 10000.

server.listen(1)

while True:
    conn, client = server.accept()

Phương thức listen() cho python biết socket này là một server, tham số của phương thức này là số lượng các kết nối có thể có trong hàng đợi, ít nhất là 0 và cao nhất là do hệ điều hành chỉ định (thường là 5). Phương thức accept() sẽ đưa server vào trạng thái chờ đợi cho đến khi có kết nối thì sẽ trả về một tuple gồm có một socket khác dùng để truyền dữ liệu qua lại với client và một tuple nữa bao gồm địa chỉ ip và port của ứng dụng client.

while True:
    data = conn.recv(1024)
    print ("Receive from client:", data)
    if data:
        print ("Response to client")
        conn.sendall(data)
    else:
        print ("No data received")
        break

Phương thức recv() sẽ đọc các byte dữ liệu có trong socket conn, tham số 1024 tức là mỗi lần chỉ đọc tối ta 1024 byte dữ liệu, nên chúng ta đặt trong vòng lặp while để có thể đọc hết những gì client gửi sang, nếu có dữ liệu gửi sang thì chúng ta gửi trả lời về client thông qua phương thức sendall()ở đây chúng ta chỉ đơn giản là gửi lại những gì client đã gửi lên server thôi.

finally:
    conn.close()

Khi đã đọc hết dữ liệu từ client, chúng ta break vòng lặp và ngắt kết nối bằng phương thức close().

Client

import socket

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((socket.gethostname(), 10000))
try:
    message = "Hello from client"
    client.sendall(message.encode('ascii'))

    amount_received = 0
    amount_expected = len(message)

    while amount_received < amount_expected:
        data = client.recv(1024)
        amount_received += len(data)
        print ("Response from server:", data)
finally:
    print ("Closing socket")
    client.close()

Chúng ta cũng tạo một đối tượng socket kết nối và gửi tin nhắn lên server.

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 10000))

Đối tượng socket ở client cũng sử dụng IP v4 và giao thức TCP. Phương thức connect() sẽ chỉ định cho python biết đây là một ứng dụng client và tạo kết nối đến server.

message = "Hello from client"
client.sendall(message.encode('ascii'))

Sau khi đã kết nối đến server, chúng ta gửi tin nhắn lên bằng phương thức sendall(), dữ liệu truyền đi chỉ là các bit mà trong Python 3 thì mặc định chuỗi luôn luôn là Unicode nên chúng ta phải mã hóa chuỗi này sang ASCII bằng phương thức encode() trước (để giải mã thì chúng ta dùng phương thức decode('ascii')).

finally:
    print ("Closing socket")
    client.close()

Cũng tương tự như server, khi đã kết thúc công việc chúng ta phải ngắt kết nối socket nếu không sẽ lãng phí tài nguyên. Mặc định thì khi kết thúc một chương trình python thì bộ thu gom rác của python đã tự động ngắt kết nối socket rồi nhưng chúng ta nên tự ngắt thì tốt hơn.

C:\User\Python>python server.py
Starting server on port 10000
Connecting from ('192.168.0.103', 56419)
Receive from client: b'Hello from client'
Response to client
Receive from client: b''
No data received
C:\User\Python\python client.py
Response from server: b'Hello from client'
Closing socket

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]

Python – File

Trong phần này chúng ta sẽ tìm hiểu về cách đọc ghi file trong Python.

Hệ thống nhập xuất chuẩn

Trong Python có 3 luồng nhập xuất là luồng input, luồng output và luồng error. Luồng input là luồng nhập, tất cả các dữ liệu đi vào chương trình đều đi qua đường này, luồng này mặc định được kết nối giữa chương trình với bàn phím. Luồng output là luồng xuất, làm nhiệm vụ đưa dữ liệu ra ngoài, mặc định gắn với màn hình console (trên Windows là cmd – Command Prompt, trên Linux là Terminal). Luồng error là luồng báo lỗi, luồng này đưa các thông báo lỗi ra ngoài, mặc định cũng gắn với màn hình console.

Luồng input

Đây là dòng dữ liệu đi vào chương trình.

import sys


print ('Enter your name: ',)

name = ''

while True:
   c = sys.stdin.read(1)
   if c == '\n':
      break
   name = name + c

print ('Your name is:', name)

Để làm việc với dòng nhập xuất thì chúng ta phải import module sys. Trong module này có đối tượng stdin với phương thức read(), phương thức này đọc các kí tự trong luồng nhập. Trong ví dụ trên, chúng ta in ra dòng chữ “Enter your name:”, sau đó cho một vòng lặp chạy, mỗi lần lặp chúng ta gọi phương thức read() trên để đọc từng kí tự vào từ luồng nhập và kiểm tra nếu kí tự vào là kí tự Enter thì thoát vòng lặp. Kí tự enter có kí hiệu là \n.

Enter your name: Jan
 Your name is: Jan

Ngoài phương thức read() ra còn có các hàm làm nhiệm vụ tương tự là input() và raw_input() .

Hàm input() in ra dòng thông báo, đọc dữ liệu vào và đánh giá  dữ liệu.

data = input('Enter expression: ')

print ('You have entered:', data)
Enter expression: 3*3
You have entered: 9

Hàm raw_input() cũng tương tự như hàm input().

Luồng output

Luồng output là luồng dữ liệu đi ra khỏi chương trình.

import sys

sys.stdout.write('Honore de Balzac, Father Goriot\n')
sys.stdout.write('Honore de Balzac, Lost Illusions\n')

Trong ví dụ trên, chúng ta in các đoạn text ra màn hình bằng phương thức write().

Honore de Balzac, Father Goriot
Honore de Balzac, Lost Illusions

Hàm print() mà chúng ta dùng lâu nay thực chất là nó gọi tới phương thức write() của đối tượng stdout.

print ('Honore de Balzac, The Splendors and Miseries of Courtesans')
print ('Honore de Balzac, Gobseck')

Chuyển hướng luồng nhập xuất vào file

Như đã nói ở đầu bài, luồng nhập xuất mặc định được gắn với màn hình và bàn phím. Tuy nhiên chúng ta có thể chuyển hướng chúng để làm việc với file.

import sys


f = open('output.txt', 'w')
sys.stdout = f

print ('Lucien')
sys.stdout.write('Rastignac\n')
sys.stdout.writelines(['Camusot\n', 'Collin\n'])

sys.stdout = sys.__stdout__

print ('Bianchon')
sys.stdout.write('Lambert\n')

Trong đoạn code trên, chúng ta chuyển hướng stdout từ màn hình sang file.

Bianchon
Lambert
Lucien 
Rastignac 
Camusot 
Collin

Hàm open

Hàm open() được dùng để mở file trong hệ thống.

open(filename, [mode='r'], [bufsize])

Hàm này nhận ba tham số đầu vào, tham số thứ nhất là tên file, tham số thứ 2 là chế độ mở, tham số thứ 3 là kích thước bộ đệm. Chế độ mở file ở đây chỉ định cách mà file được mở. Có các chế độ như mở để đọc, ghi, ghi đè… Tham số bộ đệm là tham số tùy chọn, tức là bạn không cần đưa tham số này vào, nhưng thường thì bạn cũng không cần quan tâm đến tham số này làm gì.

Dưới đây là danh sách các chế độ mở file:

Mode Meaning
r Đọc dữ liệu kiểu text
rb Đọc dữ liệu kiểu nhị phân
w Ghi dữ liệu kiểu text
wb Ghi dữ liệu nhị phân
a Ghi thêm vào cuối file
+ Ghi đè

Đoạn code đưới dây đọc nội dung một file.

f = open('ifyouwantme', 'r')

for line in f:
   print (line,)

f.close()

Chúng ta mở file ở chế độ đọc. Sau đó duyệt qua toàn bộ nội dung file bằng vòng lặp for. Cuối cùng chúng ta đóng file này lại.

Are you really here or am I dreaming
I can't tell dreams from truth 
for it's been so long since I have seen you
I can hardly remember your face anymore 

When I get really lonely
and the distance causes our silence
I think of you smiling 
with pride in your eyes a lover that sighs 

...

Trong ví dụ dưới đây, chúng ta cũng đọc file nhưng dùng phương thức readline().

f = open('ifyouwantme', 'r')

while True:
   line = f.readline()
   if not line: break
   else: print (line,)

f.close()

Ngoài ra chúng ta có phương thức readlines() (có thêm chữ s sau readline), phương thức này sử dụng đơn giản vì mỗi lần gọi, phương thức này đọc toàn bộ nội dung file vào bộ nhớ, cũng vì thế nên chúng ta nên hạn chế sử dụng phương thức này với các file có kích thước lớn.

f = open('ifyouwantme', 'r')

contents = f.readlines()

for i in contents:
   print (i,)

f.close()

Trong ví dụ tiếp theo, chúng ta sẽ ghi nội dung vào file.


text = """Incompatible, it don't matter though
'cos someone's bound to hear my cry
Speak out if you do
You're not easy to find\n"""


f = open('strophe.txt', 'w')
f.write(text)

f.close()

Để ghi file thì chỉ đơn giản là mở file ở chế độ w, sau đó dùng phương thức write() để ghi, nếu file đã tồn tại thì nội dung file sẽ bị ghi đè.

Incompatible, it don't matter though
'cos someone's bound to hear my cry
Speak out if you do
You're not easy to find

Module pickle

Bây giờ chúng ta sẽ đọc và ghi file theo kiểu dữ liệu nhị phân, ví dụ về các dạng dữ liệu nhị phân là hình ảnh, nhạc, phim… ngay cả các đoạn text thông thường cũng có cách để ghi theo kiểu nhị phân. Trong ví dụ dưới đây, chúng ta sẽ ghi dữ liệu của một đối tượng vào file. Để ghi dữ liệu dạng nhị phân thì chúng ta dùng tới module pickle. Khi ghi lên file, pickle sẽ chuyển các đoạn text thành các dạng kí hiệu, khi đọc, nó sẽ chuyển ngược lại về text cho chúng ta.


import pickle

class Person:
   def __init__(self, name, age):
      self.name = name
      self.age = age

   def getName(self):
      return self.name

   def getAge(self):
      return self.age


person = Person('Monica', 15)
print (person.getName())
print (person.getAge())

f = open('monica', 'wb')
pickle.dump(person, f)
f.close()

f = open('monica', 'r')
monica = pickle.load(f)
f.close()

print (monica.getName())
print (monica.getAge())

Trong đoạn code trên, chúng ta định nghĩa lớp Person và tạo một đối tượng với lớp này. Sau đó chúng ta ghi dữ liệu của đối tượng này vao file bằng phương thức dump(). Để đọc dữ liệu nhị phân thì chúng ta dùng phương thức load().

Monica
15
Monica
15
monica: ASCII text

 

Python – Hàm

Trong phần này chúng ta sẽ tìm hiểu về hàm trong Python.

Hàm là một đoạn lệnh trong một chương trình thực hiện một công việc nào đó. Việc dùng hàm có các lợi ích sau:

  • Giảm khối lượng mã nguồn
  • Dễ quản lý do chương trình được chia thành nhiều phần nhỏ
  • Mã nguồn trở nên sáng sủa
  • Tái sử dụng lại mã nguồn
  • Che giấu thông tin

Trong Python một hàm cũng tương đương với một đối tượng. Giá trị của hàm có thể được gán vào biến, lưu vào trong danh sách hoặc được dùng để làm tham số của các hàm khác… làm cho ngôn ngữ lập trình trở nên tinh xảo hơn.

Có hai loại hàm, thứ nhất là các hàm có sẵn ví dụ như dir(), len(), abs()… thứ hai là các hàm do chúng ta tạo ra, để tạo ra một hàm (hay còn còn là định nghĩa một hàm) thì chúng ta dùng từ khóa def.

Định nghĩa một hàm

Chúng ta định nghĩa một hàm bằng từ khóa def, phần code bên trong hàm phải thụt lùi vào.

def function():
    pass

Sau từ khóa def là tên hàm với cặp dấu ngoặc tròn () và dấu hai chấm. Bên dưới chúng ta thụt lùi vô để viết các đoạn code, phần này là phần thân hàm.

Hàm sau khi được định nghĩa xong sẽ được thực thi khi cần thiết. Trong lập trình thì chúng ta dùng từ “gọi hàm” để chỉ việc sử dụng hàm đó. Chỉ khi chúng ta gọi hàm, đoạn code bên trong thân hàm mới được thực hiện.

myfunc()

Để gọi một hàm thì chỉ đơn giản là ghi lại tên hàm đó cùng với cặp dấu ngoặc tròn ().

"""
The test.py script shows how to work with
functions in Python. 
author: PhoCode, 2016
"""

def showModuleName(): 
    print (__doc__ )

def getModuleFile(): 
    return __file__

a = showModuleName() 
b = getModuleFile() 

print (a, b)

Trong ví dụ trên, ở phần đầu chúng ta có một string giới thiệu về đoạn code trong file, đoạn string này có tác dụng giống như lời giới thiệu, mô tả về chương trình vậy. File chứa đoạn code này được gọi là module. Trong đoạn code trên, chúng ta định nghĩa hai hàm, hàm đầu tiên in ra nội dung của biến __doc__, hàm thứ hai trả về giá trị của biến __file__. Một hàm có thể có hoặc không có giá trị trả về. Nếu chúng ta không trả về một thứ gì thì mặc định Python sẽ ngầm trả về None. Hai biến __doc__ và __file__ là hai biến đặc biệt trong Python.


The test.py script shows how to work with
functions in Python. 
author: PhoCode, 2016

None C:\Users\PhoCode\test.py

Như đã nói ở trên, phần thân hàm phải viết lùi vào trong, nếu không sẽ có lỗi.


def f1():
    print ("f1()")
    
f1()
#f2()

def f2():
    print ("f2()")

Trong ví dụ trên, chúng ta định nghĩa hai hàm và gọi hai hàm đó. Nhưng một câu lệnh gọi hàm đã bị đánh dấu là chú thích vì nó được gọi trước phần định nghĩa hàm, một hàm luôn luôn phải được định nghĩa trước khi được gọi, tức là phải viết phần định nghĩa trước rồi mới đến câu lệnh gọi.

#f2()

def f2():
    print ("f2()")

Nếu muốn gọi hàm f2() thì bạn phải để câu lệnh f2() sau phần định nghĩa hàm. Nếu không sẽ báo lỗi.

Vị trí của hàm

Một hàm có thể được định nghĩa bên trong một module, một lớp hoặc bên trong một hàm khác. Hàm được định nghĩa bên trong một lớp được gọi là phương thức.


class Some:

    @staticmethod  
    def f():
        print ("f() method")

def f():
    print ("f() function")
    
def g():
    def f():
        print ("f() inner function")
    f()        
        
Some.f()
f()
g()

Trong ví dụ trên, chúng ta định nghĩa hàm ở những vị trí khác nhau.

class Some:

    @staticmethod  
    def f():
        print ("f() method")

Đoạn code trên định nghĩa một phương thức tĩnh với nhờ vào chỉ thị @staticmethod trong lớp Some.

def f():
    print ("f() function")

Đoạn code trên định nghĩa một hàm bình thường.

def g():
    def f():
        print ("f() inner function")
    f()   

Đoạn code trên định nghĩa hàm f() bên trong hàm g(), hàm được định nghĩa bên trong một hàm khác được gọi là hàm nội (inner function).

Some.f()
f()
g() 

Chúng ta gọi phương thức tĩnh bằng cách ghi tên lớp, sau đó là dấu chấm và tên phương thức cùng với cặp dấu ngoặc tròn (). Các hàm bình thường được gọi bằng cách ghi tên hàm với cặp dấu ngoặc tròn ().

f() method
f() function
f() inner function

Đối tượng hàm

Bản chất của hàm trong Python là các đối tượng. Chúng được xử lý như các đối tượng tạo ra từ các lớp.


def f():
    """This function prints a message """
    print ("Today it is a cloudy day")
    
print (isinstance(f, object))
print (id(f))
    
print (f.__doc__)
print (f.__name__)

Đoạn code trên kiểm tra tính đối tượng của hàm.

def f():
    """This function prints a message """
    print ("Today it is a cloudy day")

Đoạn code trên định nghĩa hàm f(). Hàm này in một string là màn hình. Ngoài ra hàm này còn có một đoạn string giới thiệu ở đầu nữa.

print (isinstance(f, object))

Hàm isinstance() có tác dụng kiểm tra xem hàm f() có phải là một đối tượng của lớp object hay không. Lớp object là lớp cơ sở, mọi lớp trong Python đều kế thừa từ lớp này.

print (id(f))

Mỗi đối tượng trong Python đều có một id. Hàm id() trả về id của đối tượng đó.

print (f.__doc__)
print (f.__name__)

Tất cả các đối tượng đều chứa biến bên trong nó, biến trong đối tượng được gọi là các thuộc tính. Hai thuộc tính __doc____name__ là hai thuộc tính có sẵn có các đối tượng trong Python.

True
3077407212
This function prints a message 
f

Các đối tượng có thể được lưu trữ trong các danh sách hoặc làm tham số cho các hàm khác.


def f():
    pass

def g():
    pass
  
def h(f):
    print (id(f))
  
a = (f, g, h)

for i in a:
    print (i)
    
h(f)
h(g)

Ở trên chúng ta định nghĩa 3 hàm. Sau đó chúng ta lưu chúng trong một tuple và đưa vào tham số của một hàm.

a = (f, g, h)

for i in a:
    print (i)

Trong đoạn code trên, chúng ta đưa 3 đối tượng vào một tuple và duyệt tuple đó.

h(f)
h(g)

Hai dòng trên chúng ta đưa hàm f() và g() làm tham số của hàm h().

<function f at 0xb7664fb4>
<function g at 0xb766c1b4>
<function h at 0xb766c3ac>
3076935604
3076964788

Từ khóa return

Hàm được tạo ra để thực hiện các công việc nào đó. Thường thì các công việc này phải có một kết quả nào đó. Ví dụ bạn viết một hàm làm công việc là tính tổng của một dãy số, sau khi khai báo tên hàm, bạn khai báo biến để lưu giá trị tổng rồi duyệt qua một danh sách cho trước để tính tổng rồi lưu vào biến tổng đó, nhưng khi kết thúc hàm thì biến tính tổng đó bị xóa, bạn chẳng thể nào lấy được biến tổng ở ngoài hàm đó nữa. Lúc này bạn cần dùng đến từ khóa return, từ khóa return có tác dụng “trả về” một giá trị của một hàm. Một hàm có thể có hoặc không có từ khóa return, nếu không có thì Python mặc định ngầm trả về một đối tượng None.


def showMessage(msg):
    print (msg)

def cube(x):
    return x * x * x   
   
x = cube(3)    
print (x)

showMessage("Computation finished.")
print (showMessage("Ready."))

Trong đoạn code trên, chúng ta định nghĩa hai hàm. Một hàm có return, một hàm không.

def showMessage(msg):
    print (msg)

Hàm showMessage() không trả về mọt giá trị gì mà chỉ in ra một dòng string.

def cube(x):
    return x * x * x

Hàm cube() thực hiện tính lập phương của một số và trả về giá trị của phép tính này.

x = cube(3) 

Ở dòng code trên, chúng ta gọi hàm cube(). Như đã nói ở trên hàm này sẽ trả về một giá trị, chúng ta lưu lại giá trị này bằng cách gán nó cho biến x.

showMessage("Computation finished.")

Ở dòng trên chúng ta gọi hàm showMessage() có tham số đầu vào là một string. Hàm này chỉ có nhiệm vụ in đoạn string ra màn hình chứ không trả về gì cả.

print (showMessage("Ready."))

Dòng code trên in ra hai lần, vì trong hàm showMessage() đã có lệnh in ra một string. Hàm này lại được làm đối số trong một hàm print() khác, mà vì hàm này không có câu lệnh return nào nên ngầm trả về đối tượng None nên hàm print() ở ngoài sẽ in ra None.

Định nghĩa lại hàm

Trong Python bạn có thể định nghĩa lại một hàm đã được định nghĩa sẵn, mặc dù làm thế cũng không có ý nghĩa mấy và thực tế thì trong các ngôn ngữ khác bạn không được phép làm điều này.


from time import gmtime, strftime


def showMessage(msg):
    print (msg)
        
showMessage("Ready.")

def showMessage(msg):
    print (strftime("%H:%M:%S", gmtime()),)
    print (msg)
    
showMessage("Processing.")  

Trong ví dụ trên chúng ta định nghĩa hàm showMessage() hai lần.

from time import gmtime, strftime

Ở dòng trên chúng ta import hàm gmtime(), strftime() từ module time.

def showMessage(msg):
    print (msg )       

Đây là hàm showMessage() được định nghĩa lần thứ nhất.

def showMessage(msg):
    print (strftime("%H:%M:%S", gmtime()),)
    print (msg)

Đoạn code trên là phần định nghĩa hàm showMEssage() lần thứ hai. Lần này chúng ta sử dụng một số hàm lấy thời gian trong module time.

Ready.
23:49:33 Processing.

Tham số cho hàm

Ví dụ khi bạn cần một hàm làm công việc tính tổng của một dãy số, hàm đó sẽ cần biết phải tính dãy số nào chứ? Và dãy số mà bạn “đưa” cho hàm để nó làm công việc tính toán gọi là tham số. Vậy là bâu giờ bạn biết được quá trình làm việc của một hàm thông thường rồi. Ban đầu hàm nhận vào các tham số, sau đó xử lý chúng và return lại kết quả cho chúng ta.

Untitled


def C2F(c):
    return c * 9/5 + 32

print (C2F(100))
print (C2F(0))
print (C2F(30))

Trong đoạn code trên, chúng ta viết hàm chuyển đổi nhiệt độ từ Celsius sang Fahrenheit. Hàm này nhận vào tham số là nhiệt độ ở Celsius.

212
32
86

Bạn có thể đặt giá trị mặc định cho các tham số, giá trị mặc định sẽ được sử dụng nếu bạn không đưa vào một tham số nào khi gọi hàm.


def power(x, y=2):
    r = 1
    for i in range(y):
       r = r * x
    return r

print (power(3))
print (power(3, 3))
print (power(5, 5))

Ở trên chúng ta định nghĩa hàm lấy lũy thừa. Hàm này nhận hai tham số, trong đó tham số y có giá trị mặc định là 2.

9
27
3125

Truyền tham số kiểu tham chiếu

Khi bạn đưa các tham số vào một hàm, bên trong hàm sẽ tạo một biến khác và copy giá trị của tham số truyền vào vào biến mới này, và khi kết thúc hàm thì biến này sẽ bị xóa, tham số kiểu này được gọi là tham trị. Tuy nhiên bạn có thể đưa chính biến ở ngoài vào, hàm sẽ thực hiện xử lý trên biến này luôn chứ không tạo biến mới nữa, tham số kiểu này được gọi là tham chiếu. 

def f(x):
 x = "Bonjour"
 
str = "Hello"
print (str)

f(str)

print(str)

Trong ví dụ trên, chúng ta định nghĩa hàm f(), hàm này thay đổi giá trị truyền vào thành chuỗi “Bonjour”, tuy nhiên vì là truyền theo kiểu tham trị, nên khi đưa một biến vào hàm này, giá trị của biến đó cũng không thay đổi.

Hello
Hello

Mặc định các tham số có kiểu dữ liệu dạng danh sách như list, tuple, dictionary thì các tham số này là tham chiếu.


n = [1, 2, 3, 4, 5]

print ("Original list:", n)

def f(x):
    x.pop()
    x.pop()
    x.insert(0, 0)
    print ("Inside f():", x) 
    
f(n)    

print ("After function call:", n)

Trong ví dụ trên, chúng ta truyền vào hàm f() list n, sau khi hàm này xử lý xong thì list này cũng thay đổi do hàm này xử lý trên list chứ không tạo list mới.

Original list: [1, 2, 3, 4, 5]
Inside f(): [0, 1, 2, 3]
After function call: [0, 1, 2, 3]

Biến toàn cục và biến cục bộ

Tiếp theo chúng ta tìm hiểu về biến toàn cục và biến cục bộ.


name = "Jack"

def f():
   name = "Robert"
   print ("Within function", name)

print ("Outside function", name)
f()

Một biến được định nghĩa bên trong một hàm chỉ có thể nhìn thấy bên trong hàm đó và được gọi là biến cục bộ.

Outside function Jack
Within function Robert

name = "Jack"

def f():
   print ("Within function", name)

print ("Outside function", name)
f()

Các biến được định nghĩa ngoài hàm, nói chính xác hơn là không nằm trong hàm nào thì được gọi là biến toàn cục. Biến này có thể được nhìn thấy ở bất cứ đâu.

Outside function Jack
Within function Jack

Tuy nhiên bạn có thể làm cho một biến cục bộ trở thành biến toàn cục bằng từ khóa global.


name = "Jack"

def f():
   global name 
   name = "Robert"
   print ("Within function", name)


print ("Outside function", name)
f()
print ("Outside function", name)

Trong đoạn code trên, chúng ta chỉ định cho biến name bên trong hàm là biến toàn cục, tức là bay giờ biến name đó chính là biến name đã được định nghĩa ở ngoài hàm.

global name 
name = "Robert"

Ở đoạn code trên, chúng ta dùng từ khóa global để làm cho một biến cục bộ trở thành biến toàn cục.

Outside function Jack
Within function Robert
Outside function Robert