Tư duy Tính toán

Hàm, Đặc tả & Kiểm thử

Trường ĐH Công nghệ – Đại học Quốc gia Hà Nội

Nội dung

  1. Hàm
  2. Mô-đun
  3. Đặc tả
  4. Kiểm thử

1.
Hàm

Chế độ tương tác

Read, Evaluate, Print, and Loop (REPL)

  • Thực thi lệnh & hiển thị giá trị tức thì
  • Hữu ích khi thử nghiệm nhanh

>>> x = 5
>>> y = 4
>>> bigger = max(x, y)
>>> bigger
5
>>> pi = 3.14159
>>> a = round(pi, 2)
>>> a
3.14
        

Hàm dựng sẵn

Built-in functions

VD: max()round()hàm dựng sẵn.

  • max(x, y): tìm số lớn hơn
  • round(x, n): làm tròn đến n chữ số thập phân
  • Hàm là bộ biến đổi giá trị

Hàm dựng sẵn

Built-in functions


>>> x = 5
>>> y = 4
>>> bigger = max(x, y)
>>> bigger
5
>>> pi = 3.14159
>>> a = round(pi, 2)
>>> a
3.14
        

Hàm là bộ biến đổi giá trị

Giá trị đối số đầu vào Hàm Giá trị trả về đầu ra

Một số nhóm hàm dựng sẵn

  • Chuyển kiểu: int(), float(), bool()
  • Thông tin kiểu: type()
  • Vào/ra: input(), print()
  • Tính toán cơ bản: max(), min(), abs()
  • Xem tất cả hàm dựng sẵn:

Tài liệu hàm

Làm sao biết một hàm làm gì? → Đọc tài liệu.

  • Tên hàm & tham số
  • Mô tả hành vi & giá trị trả về
  • Ví dụ sử dụng

Tài liệu hàm: ví dụ

Sử dụng help() để xem tài liệu hàm trong chế độ tương tác:


>>> help(round)
Help on built-in function round in module builtins:

round(number, ndigits=None)
    Round a number to a given precision in decimal digits.

    The return value is an integer if ndigits is omitted or None.  Otherwise
    the return value has the same type as the number.  ndigits may be negative.
        

Tài liệu hàm: ví dụ

Tài liệu hàm round() từ Python docs:

Tài liệu hàm round()

Vì sao dùng hàm?

  • Tổ chức mã theo tên có ý nghĩa
  • Dễ đọc, dễ gỡ lỗi
  • Loại bỏ lặp lại; chỉ cần sửa một chỗ khi cần
  • Dùng lại trong chương trình khác

Định nghĩa hàm của bạn

Mục tiêu: đổi inch sang feet.


def inches_to_feet(inches):
    return inches/12
        

Cú pháp định nghĩa hàm

  • Phần đầu (header) bắt đầu bằng def và kết thúc bằng :
  • Thân hàm: mã thụt lề bên dưới
  • Tham số: biến đại diện cho đầu vào

def <tên_hàm>(<tham_số1>, ...):
    <các_lệnh>
        

💡 deftừ khóa đặc biệt.

Gọi hàm vs. Định nghĩa

Định nghĩa

def inches_to_feet(inches):
    return inches/12
            
  • Tạo hàm mới, chưa thực thi
  • tham số trong dấu ngoặc
Gọi hàm

if __name__ == "__main__":
    inches_to_feet(65)
            
  • Thực thi hàm đã định nghĩa
  • đối số trong dấu ngoặc

Câu lệnh return

  • Xác định đầu ra của hàm
  • Kết thúc thực thi hàm

return <biểu_thức>
        

💡 return cũng là một từ khóa đặc biệt.

Câu hỏi thường gặp về return

Q: Sự khác nhau giữa returnprint()?

return là câu lệnh di chuyển dữ liệu trong bộ nhớ; print() là hàm hiển thị ký tự lên màn hình.

Q: Nếu một hàm không có return thì sao?

→ Việc thực thi kết thúc ở dòng cuối của hàm và trả về giá trị đặc biệt None.

Q: Nếu còn câu lệnh sau return thì sao?

→ Chúng sẽ bị bỏ qua.

Ngăn xếp lời gọi

Call stack

  • Mỗi lần gọi hàm, tạo một khung (frame) mới
  • Hàm kết thúc → frame được xoá khỏi call stack
  • Giúp Python theo dõi vị trí đang thực thi

Ngăn xếp lời gọi


def func_b():
    print("func b")

def func_a():
    print("func a")
    func_b()

if __name__ == "__main__":
    func_a()
          
Ngăn xếp lời gọi <func_b> <func_a> <main>

Biến cục bộ vs. toàn cục

  • Toàn cục: khai báo ngoài mọi hàm; truy cập khắp chương trình
  • Cục bộ: khai báo trong hàm; chỉ dùng trong hàm đó

Quy tắc truy cập

  • Hàm đọc được biến toàn cục (mặc định chỉ đọc)
  • Gán tên trong hàm → tạo biến cục bộ mới
  • Muốn sửa biến toàn cục trong hàm → dùng global
  • Nên chỉ dùng biến toàn cục cho các hằng số hoặc cấu hình chung

Ví dụ: toàn cục vs. cục bộ


counter = 0  # biến toàn cục

def show_counter():
    print("Current counter:", counter)

def bad_increment():
    counter = counter + 1  # UnboundLocalError
    print("Bad_increment:", counter)

def good_increment():
    global counter  # khai báo dùng biến toàn cục
    counter = counter + 1
    print("Good_increment:", counter)
        

2.
Mô-đun

Mô-đun

Module

Tập hợp biến & hàm để tổ chứctái sử dụng.

  • Dùng với import
  • Truy cập bằng dấu chấm: <module>.<name>

>>> import math
>>> math.pi
3.141592653589793
>>> p = math.sqrt(25)
5
        

Vì sao dùng mô-đun?

  • Tăng tốc phát triển: dùng lại mã có sẵn
  • Thư viện chuẩn & bên thứ ba (PyPI)
  • Tổ chức dự án lớn

Tạo mô-đun của bạn

Lưu mã vào tệp .py → tên mô-đun = tên tệp.


"""Thông tin UET-VNU."""
# Biến toàn cục
university_name = "UET - VNU"

def get_address():
    """Trả về địa chỉ của trường."""
    return "144 Xuân Thủy"
        

(Ví dụ: uet_info.py)

Dùng mô-đun tự viết

main.py

import uet_info

if __name__ == "__main__":
    print(uet_info.university_name)
    print(uet_info.get_address())
            
Chế độ tương tác

>>> import uet_info
>>> uet_info.university_name
'UET - VNU'
            

3.
Đặc tả

Đặc tả

Specifications

  • Thường viết dưới dạng docstring
  • Mô tả cách dùng, không phải cách cài đặt
  • Lập trình viên cần tưởng tượng mình là người dùng để viết đặc tả

Đặc tả gồm những gì?

  • Mục đích (làm gì)
  • Tham số & kiểu (+ điều kiện trước)
  • Giá trị trả về & kiểu
  • Tác dụng phụ / ngoại lệ (nếu có)
  • Ví dụ / kiểm thử

Docstring trong định nghĩa hàm


def inches_to_feet(inches):
    """
    Trả về số feet tương ứng với số inch cho trước.
    Tham số:
        inches (float|int): giá trị cần đổi.
    Trả về:
        float: feet.
    """
    return inches/12
        

Docstring dùng dấu nháy ba; hiển thị bởi help().

Ví dụ khác: circle_area


import math

def circle_area(radius):
    """
    Trả về diện tích hình tròn bán kính radius.
    Tham số:
        radius (float): bán kính, phải >= 0.
    Trả về:
        float: diện tích.
    """
    return math.pi * radius ** 2
        

4.
Kiểm thử

Vì sao cần kiểm thử?

  • Bảo đảm hành vi như kỳ vọng
  • Phát hiện lỗi sớm; tăng độ tin cậy
  • Hỗ trợ bảo trì & thay đổi về sau

Viết kiểm thử với assert


assert <điều_kiện>, "Thông báo lỗi (tuỳ chọn)"
        
  • Đúng → chương trình tiếp tục
  • Sai → phát sinh AssertionError

Ví dụ: kiểm thử qua (Passed)


x = 10
assert x > 0, "x phải dương"
        

Kết quả: điều kiện đúng → chương trình chạy bình thường.

Ví dụ: kiểm thử trượt (Failed)


x = -1
assert x > 0, "x phải dương"
        

Kết quả: điều kiện sai → AssertionError.

Viết test trước khi viết code

đặc tả → viết ca kiểm thử trước; thân hàm có thể để trống.

Phát triển hướng kiểm thử

Test-Driven Development (TDD)

  1. Viết đặc tả
  2. Tạo kiểm thử
  3. Cài đặt hàm
  4. Chạy kiểm thử
  5. Chỉnh sửa cho đến khi qua hết test cases

Khi kiểm thử thất bại?

  • Đừng hoảng & đừng sửa bừa
  • Đọc kỹ thông báo lỗi
  • Xác định ca nào thất bại & vì sao

Ví dụ: đặc tả fib(n)

Trả về số Fibonacci thứ n:


fib(0) = 0
fib(1) = 1
fib(2) = fib(1) + fib(0) = 1
fib(3) = fib(2) + fib(1) = 2
fib(n) = fib(n-1) + fib(n-2)
        

Viết kiểm thử trước cho fib


# test_fib.py
# fibonacci.py là tệp cài đặt hàm fib(n)
from fibonacci import fib

def test_first_two():
    assert fib(0) == 0
    assert fib(1) == 1

def test_small_numbers():
    assert fib(2) == 1
    assert fib(3) == 2
    assert fib(4) == 3

def test_large_number():
    assert fib(10) == 55
        

Chạy kiểm thử (đơn giản)


# test_fib.py (cuối file)
if __name__ == "__main__":
    test_first_two()
    test_small_numbers()
    test_large_number()
        

Bản đầu tiên của fib


# fibonacci.py
def fib(n):
    """
    Trả về số Fibonacci thứ n.
    Tham số:
        n (int): vị trí trong dãy Fibonacci.
    Trả về:
        int: số Fibonacci thứ n.
    """
    return 0
        

Chạy test → thất bại (luôn trả về 0).

Nhiệm vụ

Hoàn thiện fib(n) để vượt qua tất cả kiểm thử trong test_fib.py.

Tổng kết

  1. Hàm tổ chức & tái sử dụng mã; return xác định đầu ra
  2. Hàm dựng sẵn & mô-đun giúp tăng tốc phát triển
  3. Đặc tả trong docstring mô tả cách dùng
  4. Kiểm thử với assert; TDD: đặc tả → test → cài đặt → chạy test → chỉnh