--------------- <> -----------------
--- KHOA HỌC - CÔNG NGHỆ - GIÁO DỤC - VIỆC LÀM ---
--- Học để đi cùng bà con trên thế giới ---

Tìm kiếm trong Blog

Python (26) - Hàm

Bài trước: Python (25) - Xử lý ngoại lệ với try ... except
-----

26. Hàm

Khi chương trình của bạn ngày càng nhiều chức năng, việc viết hàng trăm dòng mã liên tục sẽ khiến mã nguồn trở nên rối rắm, khó đọc và khó bảo trì. Nếu có một đoạn mã cần sử dụng ở nhiều nơi (như việc tính toán hay kiểm tra dữ liệu), việc chép - dán (copy-paste) mã nhiều lần là cách làm không tối ưu.

Trong phần này, chúng ta sẽ tìm hiểu về hàm, để giúp việc lập trình được tối ưu hơn.

26.1 Định nghĩa và gọi hàm

Hàm (function) là một khối mã lệnh độc lập, được đặt tên và thực hiện một nhiệm vụ cụ thể. Bạn có thể gọi hàm chạy nhiều lần ở các vị trí khác nhau trong chương trình mà không cần phải viết lại mã nguồn.

Trong Python, bạn dùng từ khóa def để định nghĩa (tạo) một hàm.

Cú pháp:

def ten_ham(tham_so_1, tham_so_2, ...):

    """

    Docstring: Mô tả chức năng của hàm (không bắt buộc)

    """

    # Khối lệnh bên trong hàm (thụt lề 4 khoảng trắng)

    câu_lệnh

    return gia_tri_tra_ve # (Tùy chọn)


Ví dụ minh họa:

# 1. Định nghĩa hàm không có tham số và không có giá trị trả về

def xin_chao():

    print("Chào bạn đến với khóa học lập trình python!")


# Gọi hàm thực thi

xin_chao()

# 2. Định nghĩa hàm có tham số truyền vào

def chao_nguoi_dung(ten):

    print(f"Xin chào {ten}, chúc bạn một ngày vui!")


chao_nguoi_dung("Teo")

chao_nguoi_dung("Ti")


26.2 Giá trị trả về của hàm

Hàm có thể nhận dữ liệu vào (qua các tham số) và trả về kết quả cho chương trình chính bằng từ khóa return. Khi gặp câu lệnh return, hàm sẽ dừng lại ngay lập tức và chuyển giá trị đằng sau return về nơi gọi hàm.

Ví dụ minh họa:

def tinh_tong(a, b):

    tong = a + b

    return tong # Trả về kết quả


# Gọi hàm và lưu kết quả trả về vào một biến

ket_qua = tinh_tong(15, 25)

print(f"Tổng là: {ket_qua}") # Kết quả: 40


# Sử dụng trực tiếp kết quả trả về trong biểu thức

print(f"Tổng gấp đôi là: {tinh_tong(10, 20) * 2}") # Kết quả: 60


26.3 Tham số mặc định

Bạn có thể gán giá trị mặc định cho tham số khi định nghĩa hàm. Nếu người dùng gọi hàm mà không truyền giá trị cho tham số đó, Python sẽ tự động dùng giá trị mặc định.

Ví dụ minh họa:

def luy_thua(co_so, mu=2):

    return co_so ** mu


print(luy_thua(5))    # Không truyền 'mu', tự động lấy mu=2 -> Kết quả: 25

print(luy_thua(2, 3)) # Truyền 'mu=3' -> Kết quả: 8


26.4 Phạm vi của biến

Biến trong Python được chia thành hai loại phạm vi chính dựa trên vị trí nơi nó được khai báo:

1. Biến cục bộ

Biến cục bộ (local variable) là biến được khai báo bên trong một hàm. Biến này chỉ tồn tại và được sử dụng trong nội bộ hàm đó. Khi hàm kết thúc, biến cục bộ sẽ bị xóa khỏi bộ nhớ.

Ví dụ:

def tinh_hieu():

    x = 10 # x là biến cục bộ

    print(f"Bên trong hàm x = {x}")


tinh_hieu()

# print(x) # Lỗi! NameError: name 'x' is not defined (vì x không tồn tại ngoài hàm)


2. Biến toàn cục

Biến toàn cục (global variable) là biến được khai báo bên ngoài tất cả các hàm. Biến này có thể được đọc từ bất kỳ đâu trong chương trình.

Ví dụ:

diem_so = 10 # Biến toàn cục


def hien_thi_diem():

    print(f"Điểm số hiện tại: {diem_so}") # Đọc biến toàn cục


hien_thi_diem()


Lưu ý: Nếu bạn muốn thay đổi giá trị của biến toàn cục từ bên trong một hàm, bạn phải sử dụng từ khóa global.

Ví dụ:

bo_dem = 0 # Biến toàn cục


def tang_bo_dem():

    global bo_dem # Khai báo sử dụng biến toàn cục bo_dem

    bo_dem += 1


tang_bo_dem()

tang_bo_dem()

print(f"Bộ đếm: {bo_dem}") # Kết quả: 2


26.5 Bài tập và câu hỏi

Bài tập

Bài 26a: Xây dựng các hàm toán học cho Máy tính cầm tay (calculator)

Mục tiêu: Chia nhỏ bài toán máy tính cầm tay thành các hàm chức năng xử lý riêng biệt.

Yêu cầu: Viết các hàm thực hiện từng tác vụ toán học cơ bản:

- cong(a, b): Trả về a+b

- tru(a, b): Trả về a-b

- nhan(a, b): Trả về ab

- chia(a, b): Trả về a/b (sử dụng try ... except để xử lý chia cho 0).

Viết chương trình cho người dùng chọn phép tính và nhập hai số để gọi hàm tương ứng.

Bài 26b: Hàm định dạng dòng lịch sử phép tính

Mục tiêu: Luyện tập viết hàm xử lý chuỗi và trả về giá trị.

Yêu cầu: Viết hàm tao_chuoi_lich_su(so_a, phep_tinh, so_b, ket_qua) nhận vào 4 tham số và trả về một chuỗi có cấu trúc chuẩn để lưu tập tin.

Ví dụ: Nhập (5, "+", 3, 8) Trả về chuỗi "5 + 3 = 8".

Bài 26c: Máy tính nâng cao hoàn chỉnh modular hóa

Mục tiêu: Tổng hợp kiến thức về hàm, try ... except, và thao tác trên tập tin.

Yêu cầu: Viết ứng dụng máy tính bỏ túi hoàn chỉnh sử dụng các hàm:

1. luu_lich_su(dong_van_ban): Ghi kết quả phép tính vào file lich_su.txt

2. xem_lich_su(): Đọc và hiển thị tất cả phép tính đã lưu từ tập tin lich_su.txt. Dùng try ... except bắt lỗi nếu chưa có tập tin

3. main(): Hàm chính quản lý menu lựa chọn (1: Tính toán, 2: Xem lịch sử, 3: Thoát)

Câu hỏi ôn tập

Câu 26.1: Từ khóa nào được sử dụng để định nghĩa một hàm trong ngôn ngữ Python?

A. function

B. def

C. func

D. create

Câu 26.2: Phát biểu nào sau đây về từ khóa return trong hàm là SAI?

A. return dùng để trả kết quả của hàm về nơi gọi hàm

B. Khi gặp câu lệnh return, hàm sẽ lập tức kết thúc thực thi

C. Một hàm bắt buộc phải chứa câu lệnh return

D. Một hàm có thể không có câu lệnh return (trả về None)

Câu 26.3: Tác dụng của từ khóa global bên trong một hàm là gì?

A. Tạo ra một biến mới hoàn toàn

B. Cho phép hàm thay đổi giá trị của một biến toàn cục đã khai báo bên ngoài

C. Khai báo hàm đó có thể gọi ở mọi tập tin

D. Chuyển kiểu dữ liệu của biến sang kiểu chuỗi

26. Functions

As your program grows and gains more functionality, writing hundreds of lines of continuous code will make your source code messy, difficult to read, and hard to maintain. If a block of code needs to be used in multiple places (such as performing a calculation or validating data), copying and pasting code repeatedly is an inefficient approach.

In this section, we will learn about functions to optimize our programming practice.

26.1 Function Definition and Invocation

A function is an independent block of code that is named and performs a specific task. You can call a function to execute multiple times at different locations throughout the program without rewriting the source code.

In Python, you use the def keyword to define (create) a function.

Syntax:

def function_name(parameter_1, parameter_2, ...):

    """

    Docstring: Description of the function's task (optional)

    """

    # Code block inside the function (indented by 4 spaces)

    statement

    return return_value  # (Optional)

Examples:

# 1. Define a function with no parameters and no return value

def greet():

    print("Welcome to the Python programming course!")


# Call the function to execute

greet()


# 2. Define a function with input parameters

def greet_user(name):

    print(f"Hello {name}, have a great day!")


greet_user("John")

greet_user("Alice")

26.2 Return Values

A function can receive input data (via parameters) and return a result back to the main program using the return keyword. When a return statement is encountered, the function terminates immediately and sends the value following return back to the caller.

Examples:

def calculate_sum(a, b):

    total = a + b

    return total  # Return the result


# Call the function and store the returned value in a variable

result = calculate_sum(15, 25)

print(f"Total is: {result}")  # Result: 40


# Use the returned value directly within an expression

print(f"Doubled total is: {calculate_sum(10, 20) * 2}")  # Result: 60

26.3 Default Parameters

You can assign default values to parameters when defining a function. If the user calls the function without passing a value for that parameter, Python will automatically use the default value.

Examples:

def power(base, exponent=2):

    return base ** exponent


print(power(5))     # 'exponent' is omitted, defaults to exponent=2 -> Result: 25

print(power(2, 3))  # 'exponent=3' is passed -> Result: 8

26.4 Variable Scope

Variables in Python are categorized into two main scope levels based on where they are declared:

1. Local Variables 

A local variable is declared inside a function. It exists and can only be accessed within that function's local environment. When the function terminates, the local variable is deleted from memory.

def calculate_difference():

    x = 10  # x is a local variable

    print(f"Inside the function x = {x}")

calculate_difference()

# print(x) # Error! NameError: name 'x' is not defined (x does not exist outside the function)

2. Global Variables 

A global variable is declared outside of all functions. It can be read from anywhere within the program.

score = 10  # Global variable

def display_score():

    print(f"Current score: {score}")  # Reads the global variable

display_score()

Note: If you want to modify the value of a global variable from inside a function, you must use the global keyword.

counter = 0  # Global variable


def increment_counter():

    global counter  # Declare use of the global variable counter

    counter += 1


increment_counter()

increment_counter()

print(f"Counter: {counter}")  # Result: 2

26.5 Exercises and Review Questions

Exercises

Exercise 26a: Implement Mathematical Functions for a Calculator

Objective: Modularize a calculator application into individual functional helper routines

Requirements: Write functions to perform basic mathematical operations:

- add(a, b): Returns a + b

- subtract(a, b): Returns a - b

- multiply(a, b): Returns a * b

- divide(a, b): Returns a / b (use try ... except to handle division by zero)

Write a main routine prompting the user to select an operation and enter two numbers to invoke the corresponding function.

Exercise 26b: Format Calculation History Line

Objective: Practice writing functions that perform string formatting and return values

Requirements: Create a function format_history_line(num_a, operator, num_b, result) accepting four parameters and returning a standardized formatted string suitable for file storage

Example: Passing (5, "+", 3, 8) returns the string "5 + 3 = 8"

Exercise 26c: Complete Modularized Advanced Calculator

Objective: Combine concepts of functions, try ... except, and file handling operations.

Requirements: Build a functional pocket calculator application using the following functions:

1. save_history(text_line): Appends the calculation result string to history.txt

2. view_history(): Reads and outputs all stored calculations from history.txt. Use try ... except to handle file-not-found scenarios

3. main(): Top-level control loop managing menu choices (1: Calculate, 2: View History, 3: Exit)

Review Questions

Question 26.1: Which keyword is used to define a function in Python?

A. function

B. def

C. func

D. create

Question 26.2: Which statement regarding the return keyword in a function is INCORRECT?

A. return passes the result of a function back to the caller

B. When a return statement is encountered, the function immediately terminates execution

C. A function is strictly required to contain a return statement

D. A function can lack a return statement (implicitly returning None)

Question 26.3: What is the purpose of using the global keyword inside a function?

A. Instantiates an entirely new variable

B. Permits the function to modify the value of an existing global variable declared outside

C. Declares that the function can be called across all project files

D. Casts the data type of the variable into a string.

-----
Bài sau:

Python (25) - Xử lý ngoại lệ với try ... except

Bài trước: Python (24) - Bài tập thao tác với tập tin văn bản
-----

25. Xử lý ngoại lệ với Try ... Except

Trong quá trình viết chương trình, ngoài những lỗi về cú pháp (syntax error) làm chương trình không thể chạy ngay từ đầu, bạn sẽ gặp phải những lỗi xuất hiện khi chương trình đang chạy, được gọi là ngoại lệ (exception) hoặc lỗi runtime.

Ví dụ, khi bạn yêu cầu người dùng nhập một số nguyên, nhưng họ lại nhập vào chữ cái, hoặc khi chương trình thực hiện phép chia cho số 0, hay mở một tập tin không tồn tại. Nếu không được xử lý, chương trình sẽ lập tức bị ngừng đột ngột và hiển thị dòng báo lỗi.

Để chương trình hoạt động mượt mà, chuyên nghiệp và không bị dừng đột ngột khi gặp lỗi, Python cung cấp cấu trúc try ... except.

25.1 Cấu trúc cơ bản của try ... except

Cấu trúc try ... except hoạt động theo cơ chế "thử và bắt lỗi":

- try: Khối lệnh chứa các câu lệnh có nguy cơ phát sinh lỗi khi chạy

- except: Khối lệnh sẽ được kích hoạt và thực thi nếu có lỗi phát sinh trong khối try

Cú pháp:

try:

    # Khối lệnh thực thi (có nguy cơ xảy ra lỗi)

    câu_lệnh_1

    câu_lệnh_2

except:

    # Khối lệnh xử lý khi có lỗi xuất hiện trong khối try

    xử_lý_khi_gặp_lỗi

25.2 Bắt các loại ngoại lệ cụ thể

Một khối try có thể phát sinh nhiều loại lỗi khác nhau. Việc bắt đúng từng loại lỗi giúp bạn đưa ra thông báo chính xác cho người dùng.

Một số lỗi phổ biến trong Python:

- ValueError: Lỗi giá trị, ví dụ: ép kiểu chuỗi "abc" sang số nguyên int("abc")

- ZeroDivisionError: Lỗi chia cho số 0

- FileNotFoundError: Lỗi không tìm thấy tập tin

- TypeError: Lỗi do thao tác trên kiểu dữ liệu không phù hợp

Ví dụ, bạn hãy lập trình và chạy đoạn mã sau:

try:

    so_a = int(input("Nhập số bị chia: "))

    so_b = int(input("Nhập số chia: "))

    ket_qua = so_a / so_b

    print(f"Kết quả {so_a} / {so_b} = {ket_qua}")

except ValueError:

    print("Lỗi: Bạn phải nhập vào một số nguyên hợp lệ!")


except ZeroDivisionError:

    print("Lỗi: Không thể thực hiện phép chia cho số 0!")

except Exception as e:

    # Bắt tất cả các loại lỗi còn lại chưa dự đoán trước

    print(f"Đã xảy ra lỗi không xác định: {e}")

Bạn hãy chạy chương trình trên và nhập vào giá trị theo các tình huống sau, để quan sát các lỗi:

[Tình huống 1]: chương trình chạy bình thường

- Số bị chia: 10

- Số chia: 5

[Tình huống 2]: lỗi chia cho số 0

- Số bị chia: 10

- Số chia: 0

[Tình huống 3]: lỗi ép kiểu từ chuỗi thành số

- Số bị chia: 10

- Số chia: abc

25.3 Sử dụng Else và Finally

Python cho phép bổ sung thêm hai khối lệnh else và finally cho try … except:

- else: Chạy khi khối try thực thi thành công và không phát sinh bất kỳ lỗi nào

- finally: Luôn luôn chạy dù chương trình có xảy ra lỗi hay không. Thường dùng để giải phóng tài nguyên (như đóng tập tin, đóng kết nối cơ sở dữ liệu)

Bạn hãy viết đoạn mã thao tác với tập tin:

try:

    f = open("du_lieu.txt", "r", encoding="utf-8")

except FileNotFoundError:

    print("Lỗi: Tập tin 'du_lieu.txt' không tồn tại trên hệ thống!")

else:

    # Khối lệnh này chỉ chạy khi mở file thành công

    noi_dung = f.read()

    print("Nội dung tập tin:")

    print(noi_dung)

    f.close()

finally:

    print("Hoàn tất quá trình xử lý tập tin.")

Chạy tập tin theo các tình huống sau:

[Tình huống 1]: lỗi không có tập tin

- Bạn chạy tập tin mã nguồn, nhưng chưa tạo tập tin du_lieu.txt

[Tình huống 2]: chương trình chạy không có lỗi

- Bạn chạy tập tin mã nguồn, tạo tập tin du_lieu.txt, tập tin du_lieu.txt có dữ liệu (ví dụ: chao ban Teo). Lưu ý: tập tin du_lieu.txt phải cùng thư mục với tập tin mã nguồn.

25.4 Bài tập và câu hỏi

Bài tập 

Bài 25a: Nhập số an toàn cho Máy tính cầm tay

Mục tiêu: Đảm bảo chương trình không bị dừng đột ngột khi người dùng nhập dữ liệu sai.

Yêu cầu: Viết đoạn mã dùng vòng lặp while kết hợp try ... except để yêu cầu người dùng nhập vào một số thực. Nếu người dùng nhập sai (ví dụ nhập chữ "abc"), chương trình phải thông báo lỗi và yêu cầu nhập lại cho đến khi nhận được một số thực hợp lệ.

Bài 25b: Chia an toàn

Mục tiêu: Thực hành bắt nhiều ngoại lệ trong bài toán thực tế.

Yêu cầu: Viết chương trình máy tính thực hiện phép chia hai số ab nhập từ bàn phím. Sử dụng try ... except để xử lý triệt để hai trường hợp lỗi:

1. Người dùng nhập ký tự không phải là số (ValueError)

2. Số chia b=0 (ZeroDivisionError)

In kết quả phép tính ra màn hình nếu không có lỗi xảy ra.

Bài 25c: Nhật ký tính toán và Đọc dữ liệu lịch sử

Mục tiêu: Kết hợp try ... except với thao tác làm việc trên tập tin văn bản.

Yêu cầu: Viết chương trình mở tập tin lich_su_may_tinh.txt để đọc lịch sử các phép tính, xuất ra màn hình. Dùng try ... except để xử lý trường hợp tập tin chưa tồn tại (chương trình sẽ thông báo: "Chưa có lịch sử tính toán nào được lưu!" thay vì dừng chương trình và báo lỗi).

Câu hỏi

Câu 25.1: Khối lệnh nào trong cấu trúc try ... except sẽ luôn luôn được thực thi dù chương trình có phát sinh lỗi hay không?

A. try

B. except

C. else

D. finally

Câu 25.2: Khi thực hiện phép tính x = 10 / 0, Python sẽ phát sinh loại ngoại lệ (Exception) nào sau đây?

A. ValueError

B. ZeroDivisionError

C. IndexError

D. FileNotFoundError

Câu 25.3: Cho đoạn mã sau:

try:

    val = int("123a")

    print("Chuyển đổi thành công!")

except ValueError:

    print("Lỗi chuyển đổi dữ liệu!")

else:

    print("Thực thi thành công!")

Kết quả in ra màn hình là gì?

A. Chuyển đổi thành công!

B. Lỗi chuyển đổi dữ liệu!

C. Thực thi thành công!

D. Lỗi chuyển đổi dữ liệu! / Thực thi thành công!

Here is the complete English translation of your text, using standard programming terminology (Python conventions, standard identifier naming, and accurate terms like runtime errors, type casting, exceptions, and resource cleanup).

25. Exception Handling with Try ... Except

In addition to syntax errors that prevent a program from executing from the start, you will also encounter errors that occur while the program is running. These are called exceptions or runtime errors.

For example, when you ask a user to enter an integer but they enter text, when a program attempts to divide by zero, or when opening a non-existent file. If left unhandled, these errors cause the program to crash immediately and display an error trace.

To keep your program running smoothly and professionally without sudden crashes when encountering errors, Python provides the try ... except structure.

25.1 Basic Structure of try ... except

The try ... except construct operates on a "try and catch" mechanism:

- try: A block containing code statements that risk throwing an error during execution

- except: A block triggered and executed only if an error occurs inside the try block

Syntax:

try:

    # Code block to execute (risks raising an error)

    statement_1

    statement_2

except:

    # Code block to handle errors raised in the try block

    handle_error

25.2 Handling Specific Exception Types

A single try block can raise various types of errors. Catching specific exception types allows you to provide accurate error messages to the user.

Common Built-in Exceptions in Python:

- ValueError: Raised when a function receives an argument of the correct type but an inappropriate value (e.g., type casting a string "abc" to an integer: int("abc"))

- ZeroDivisionError: Raised when dividing a number by zero

- FileNotFoundError: Raised when trying to access a file that does not exist

- TypeError: Raised when an operation is applied to an inappropriate data type

Example Code:

try:

    dividend = int(input("Enter dividend: "))

    divisor = int(input("Enter divisor: "))

    result = dividend / divisor

    print(f"Result: {dividend} / {divisor} = {result}")

except ValueError:

    print("Error: You must enter a valid integer!")

except ZeroDivisionError:

    print("Error: Division by zero is not allowed!")

except Exception as e:

    # Catch any other unexpected errors

    print(f"An unexpected error occurred: {e}")

Run the program and test the following scenarios to observe error handling:

[Scenario 1]: Normal Execution

- Dividend: 10

- Divisor: 5

[Scenario 2]: Division by Zero Error

- Dividend: 10

- Divisor: 0

[Scenario 3]: Type Casting Error

- Dividend: 10

- Divisor: abc

25.3 Using else and finally

Python allows extending try ... except with else and finally clauses:

- else: Executes only if the try block succeeds without raising any exceptions

- finally: Always executes regardless of whether an error occurred. Commonly used for resource cleanup (e.g., closing file streams, releasing database connections).

Example Code:

try:

    f = open("data.txt", "r", encoding="utf-8")

except FileNotFoundError:

    print("Error: The file 'data.txt' does not exist on the system!")

else:

    # Executes only if the file opened successfully

    content = f.read()

    print("File Content:")

    print(content)

    f.close()

finally:

    print("File processing complete.")

Run the program under the following conditions:

[Scenario 1]: File Not Found Error

- Run the script without creating data.txt

[Scenario 2]: Successful Execution

- Create data.txt with content (e.g., "Hello World") in the same directory as the script, then run it.

25.4 Exercises and Questions

Exercises

Exercise 25a: Safe Input for Handheld Calculator

- Goal: Prevent abrupt program termination on invalid user input

- Requirement: Write a program using a while loop combined with try ... except to prompt the user to enter a float. If the input is invalid (e.g., entering "abc"), print an error message and re-prompt until a valid float is provided

Exercise 25b: Safe Division

- Goal: Practice handling multiple exceptions in a practical context

- Requirement: Write a calculator script that performs division of two numbers, a and b, entered from the keyboard. Use try ... except to fully handle two error cases:

  + User inputs non-numeric characters (ValueError)

    + Divisor b=0 (ZeroDivisionError)

    + Print the calculation result if no errors occur

Exercise 25c: Calculation Log & History Reader

- Goal: Combine try ... except with file handling

- Requirement: Write a program to open calculator_history.txt, read previous calculation logs, and display them on screen. Use try ... except to handle cases where the file does not exist yet (print "No calculation history found!" instead of throwing an error and crashing)

Quiz Questions

Question 25.1: Which block in a try ... except structure always executes, regardless of whether an exception occurred?

A. try

B. except

C. else

D. finally

Question 25.2: What exception type does Python raise when executing x = 10 / 0?

A. ValueError

B. ZeroDivisionError

C. IndexError

D. FileNotFoundError

Question 25.3: Consider the following code snippet:

try:

    val = int("123a")

    print("Conversion successful!")

except ValueError:

    print("Data conversion error!")

else:

    print("Executed successfully!")

What is the output displayed on the console?

A. Conversion successful!

B. Data conversion error!

C. Executed successfully!

D. Data conversion error! / Executed successfully!

-----
Bài sau: Python (26) - Hàm