0% found this document useful (0 votes)
8 views47 pages

Hàm Python

The document provides an overview of functions and parameters in Python, detailing their definitions, syntax, and usage. It explains how to define and call functions, the rules for naming them, the concept of return values, and the use of default parameters. Additionally, it covers various ways to pass arguments to functions, including positional, keyword, and the use of *args and **kwargs.

Uploaded by

minhchuong209
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views47 pages

Hàm Python

The document provides an overview of functions and parameters in Python, detailing their definitions, syntax, and usage. It explains how to define and call functions, the rules for naming them, the concept of return values, and the use of default parameters. Additionally, it covers various ways to pass arguments to functions, including positional, keyword, and the use of *args and **kwargs.

Uploaded by

minhchuong209
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

HÀM VÀ THAM SỐ CỦA HÀM TRONG PYTHON

A. Hàm (Function)
1. Khái niệm
- Hàm là một khối mã có tên thực hiện một nhiệm vụ cụ thể,
có thể nhận tham số và trả về giá trị. Dùng hàm để tách
logic, tái sử dụng và đọc code dễ hơn.
2. Cú pháp khai báo một hàm (Syntax)
- Từ khóa def: bắt đầu định nghĩa hàm
- ten_ham: tên hàm do lập trình viên đặt
- tham_so1, tham_so2,…: các tham số của hàm
- thân hàm: viết lệnh cho hàm
- return ket_qua: trả về biến ket_qua

3. Gọi hàm (Calling Function)


- Gán kết quả trả về của hàm ten_ham cho biến kq
4. Quy tắc đặt tên hàm (Function Name)
- Đặt tên theo quy tắc snake_case (các từ được phân cách
bằng dấu gạch dưới ( _ ) (Ví dụ: tinh_tong)
- Tên hàm phải bắt đầu bằng chữ cái hoặc dấu gạch dưới ( _ ),
trong tên chỉ chứa chữ cái, số và dấu gạch dưới ( _ )
- Tên hàm phân biệt chữ hoa và chữ thường (Ví dụ: tinh_tong
và tinh_Tong là hai hàm khác nhau)
5. Giá trị trả về (Return Values)
- Sau khi gọi hàm, kết quả sẽ được trả về cho nơi gọi hàm
dùng câu lệnh return
- Sau khi gặp lệnh return, hàm sẽ ngừng thực thi
- Có thể trả về nhiều giá trị (thực chất là tuple). Nếu không có
lệnh return, hàm sẽ mặc định trả về giá trị None

Ví dụ 1 (gọi một bạn trong lớp trả lời): Cho chương trình như
hình. Dự đoán kết quả của x
Kết quả: x sẽ là None. Vì hàm tong không có câu lệnh return.
Mặc định, kết quả trả về là None

Ví dụ 2 (gọi một bạn trong lớp trả lời): Cho chương trình như
hình. Dự đoán kết quả của x và y

Kết quả: 2 1
Giải thích: Kết quả trả về nhiều giá trị, mỗi giá trị là một
phép toán lần lượt là a // b và a % b. Do đó kết quả là 2 1
6. Câu lệnh pass (pass Statement)
- Câu lệnh pass là một placeholder, được dùng khi định nghĩa
hàm nhưng không có lệnh trong thân hàm (thân hàm rỗng).
- Điều này là do trong Python, một hàm phải có lệnh trong
thân hàm, không được để trống
7. Hàm lồng trong hàm (Function Inside Function)
- Một hàm được định nghĩa bên trong một hàm khác thường
được gọi là inner function (hoặc nested function), hàm bên
ngoài hàm inner thường được gọi là outer function. Hàm
inner có thể truy cập các biến bên trong phạm vi của hàm
outer
- Công dụng của hàm lồng trong hàm
+ Nhờ cơ chế closure, hàm inner có thể ghi nhớ biến của
hàm outer, kể cả hàm outer đã ngừng thực thi (ứng dụng
nhiều trong hàm decorator,…)
+ Giúp chia nhỏ code nhưng vẫn giữ được logic (Ví dụ: thay
vì nhiều đoạn lệnh điều kiện lồng nhau thì có thể dùng hàm
lồng nhau để tách điều kiện ra để dễ xử lí

Ví dụ (gọi một bạn trong lớp trả lời): Cho chương trình như
hình. Dự đoán kết quả của lệnh print(f(5))
Kết quả: 15
Giải thích:
+ Hàm inner(y) nằm trong hàm outer(x), khi gọi hàm
inner(y), kết quả trả về sẽ là x + y (do cơ chế closure, hàm
trong sẽ ghi nhớ biến hàm ngoài)
+ Khi gán f = outer(10), lúc này f là hàm inner(y) nhưng x =
10 đã được hàm inner ghi nhớ
+ Khi thực hiện lệnh print(f(5)), f(5) lúc này thực chất là gọi
hàm inner(5) với biến x = 10
+ Do đó, kết quả đầu ra sẽ là x + y = 10 + 5 = 15
8. Hàm vô danh (Anonymous Functions)
- Hàm vô danh (anonymous function hay còn gọi là lambda
function) là hàm được định nghĩa nhưng ko có tên cụ thể.
- Hàm này được định nghĩa bằng cách dùng từ khóa lambda
- Công dụng của hàm này là cho những chương trình gọn hơn,
khi ta chỉ cần một hàm đơn giản trong thời gian ngắn mà
không cần phải định nghĩa một hàm mới
- Cú pháp hàm lambda: lambda tham_so : bieu_thuc

9. Lợi ích của việc dùng hàm


- Tái sử dụng code nhiều lần
- Dễ dàng trong lập trình module
- Giúp code dễ đọc và dễ sửa lỗi
B. Tham số và Đối số (Parameters and Arguments)
1. Tham số
a) Khái niệm
- Tham số (parameter) là biến xuất hiện trong định nghĩa
hàm, trong dấu ngoặc đơn và ngăn cách bởi dấu phẩy
- Một hàm có thể có 1 tham số, nhiều tham số hoặc không có
tham số
b) Giá trị tham số mặc định (Default Parameters Values)
- Đặt một tham số bằng một giá trị ở trong phần định nghĩa
hàm
- Khi tham số đó không được truyền vào, nó sẽ lấy giá trị mặc
định ở phần định nghĩa
Ví dụ (gọi một bạn trả lời): Cho chương trình như hình. Dự
đoán kết quả của lệnh print(cong(3))?

Kết quả: 4
Giải thích: Khi ta không truyền đối số cho b, nó sẽ lấy giá trị
mặc định là 1. Do đó kết quả là a + b = 3 + 1 = 4

- Lưu ý: khi dùng kiểu dữ liệu mutable object (list, dictionary,


set), thì không nên dùng làm giá trị mặc định. Lý do:
+ Vì giá trị mặc định chỉ được khởi tạo khi định nghĩa hàm,
không phải mỗi lần gọi hàm
+ Nếu giá trị mặc định là một mutable object, thì tất cả các
lần gọi hàm sau sẽ dùng chung cùng một object
+ Điều này là vô cùng nguy hiểm vì có thể cho ra kết quả
không như mong muốn, do dữ liệu có thể bị thay đổi bất
ngờ qua các lần gọi hàm (do kiểu dữ liệu mutable object
vẫn có thể thay đổi nội dung sau khi khai báo)
Ví dụ (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán đầu ra của chương trình

Kết quả đầu ra:


Giải thích:
+ Ta đang muốn mỗi lần gọi hàm, thì danh sách lst sẽ rỗng.
+ Nhưng qua mỗi lần gọi hàm, lst không được tạo mới, mà là
dùng lại lst cũ
+ Điều này là do lst là một mutable object, dẫn đến các lần
gọi hàm sau đều dùng chung một object, không tạo ra một
object mới
* Cách khắc phục (có thể kêu một bạn lên viết): thay vì dùng
mutable object trực tiếp, ta thường dùng giá trị None làm
giá trị mặc định, rồi khởi tạo trong thân hàm
Kết quả đầu ra:
Giải thích:
+ Mỗi lần gọi hàm, ta sẽ kiểm tra lst và tạo một object mới
trong hàm.
+ Điều này có thể khắc phục tình trạng dùng lại object cũ là
do None là một kiểu immutable object (là kiểu dữ liệu
không thể thay đổi nội dung sau khi khởi tạo), giúp tránh
được việc dùng lại object cũ
2. Đối số
a) Khái niệm: đối số (arguments) là giá trị truyền vào khi gọi
hàm
b) Số lượng tham số và đối số:
- Trong định nghĩa hàm, có bao nhiêu tham số thì phải có bấy
nhiêu đối số truyền vào
- Thừa hay thiếu đối số truyền vào sẽ gây lỗi chương trình (trừ
việc sử dụng giá trị tham số mặc định hoặc dùng đối số
*args/**kwargs)
c) Truyền đối số theo từ khóa (Keyword Arguments)
- Truyền đối số theo từ khóa là cách truyền giá trị cho tham số
của hàm bằng cách chỉ rõ tên tham số cùng với giá trị
- Cú pháp: ten_tham_so = gia_tri
- Qua cách truyền này, vị trí của tham số sẽ không còn quan
trọng, miễn là đúng tên tham số
- Cách truyền này giúp code dễ đọc, rõ ràng, tránh được việc
truyền nhầm đối số với hàm có nhiều tham số

Ví dụ 1 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra

Kết quả: hello world


Giải thích:
+ Hàm func có hai tham số là str1 và str2. Trong thân hàm sẽ
in lần lượt là str1 và str2
+ Khi gọi hàm, ta đã sử dụng cú pháp của truyền đối số theo
từ khóa (func(str1 = “hello”, str2 = “world”))
+ Do đó, kết quả đầu ra sẽ là hello world

Ví dụ 2 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra
Kết quả đầu ra: world hello
Giải thích:
+ Hàm func có hai tham số là str1 và str2. Trong thân hàm sẽ
in lần lượt là str1 và str2
+ Khi gọi hàm, ta đã sử dụng cú pháp của truyền đối số theo
từ khóa (func(str2 = “hello”, str1 = “world”))
+ Do đó, kết quả đầu ra sẽ là world hello
d) Truyền đối số theo vị trí (Positional Arguments)
- Truyền đối số theo vị trí là cách truyền giá trị cho tham số
của hàm dựa vào thứ tự mà chúng được định nghĩa
- Theo cách này, đối số truyền vào không cần ghi tên tham số,
chỉ cần đúng vị trí
- Nếu truyền sai vị trí, kết quả có thể không như mong muốn

Ví dụ (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra
Kết quả:
Giải thích: hàm func nhận tham số ten, tuoi. Truyền đối số
theo đúng thứ tự là ten là Thai, tuoi là 17. Mỗi dòng dùng
lệnh print nên kết quả in ra 2 dòng
e) Kết hợp truyền đối số theo từ khóa và theo vị trí
- Quy tắc kết hợp giữa hai cách truyền:
+ Truyền theo vị trí phải đứng trước truyền theo từ khóa,
nếu truyền ngược lại -> chương trình sẽ báo lỗi cú pháp
+ Một tham số chỉ được gán một lần. Không thể vừa truyền
theo vị trí vừa truyền theo từ khóa cho một tham số

Ví dụ 1 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra
Kết quả:

Giải thích: đối số ten được truyền theo từ khóa, nhưng theo
quy tắc, truyền theo vị trí phải đứng trước truyền theo từ
khóa. Nên chương trình đã báo lỗi cú pháp

Ví dụ 2 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra

Kết quả:
Giải thích: ta truyền đối số tuoi hai lần nên chương trình báo
lỗi cú pháp
f) Truyền đối số chỉ theo từ khóa (Keyword-Only Arguments)
- Keyword-Only Arguments là các đối số mà bắt buộc phải
truyền theo từ khóa khi gọi hàm, không thể truyền theo vị trí
- Cách định nghĩa hàm có sử dụng keyword-only arguments:
+ Trong phần định nghĩa hàm, ta dùng dấu * để đánh dấu
rằng sau dấu * phải truyền theo từ khóa, còn trước dấu * thì
có thể truyền theo 2 cách tùy ý
+ Tất cả tham số đứng sau dấu * sẽ trở thành keyword-only

Ví dụ 1 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra

Kết quả:
Giải thích: hàm func có sử dùng dấu *, cho biết rằng các
tham số đằng sau phải truyền theo từ khóa. Ta đã truyền
tuoi và thanh_pho theo từ khóa nên chương trình chạy bình
thường

Ví dụ 2 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra
Kết quả:

Giải thích: hàm func có sử dùng dấu *, cho biết rằng các
tham số đằng sau phải truyền theo từ khóa. Nhưng tuoi và
thanh_pho lại truyền theo vị trí. Do đó, chương trình báo lỗi
TypeError
g) Truyền đối số chỉ theo vị trí (Positional-Only Arguments)
- Positional-Only Arguments là các đối số mà bắt buộc phải
truyền theo vị trí khi gọi hàm, không thể truyền theo từ khóa
- Cách định nghĩa hàm có sử dụng positional-only arguments:
+ Trong phần định nghĩa hàm, ta dùng dấu / để đánh dấu
rằng trước dấu / phải truyền theo vị trí, còn sau dấu / thì
truyền 2 cách tùy ý
+ Tất cả tham số đứng trước dấu / sẽ trở thành positional-
only

Ví dụ 1 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra
Kết quả:
Giải thích: hàm func có sử dùng dấu /, cho biết rằng các
tham số đằng trước phải truyền theo vị trí. Ta đã truyền ten,
tuoi theo vị trí nên chương trình chạy bình thường

Ví dụ 2 (gọi một bạn trong lớp trả lời): Cho chương trình sau.
Dự đoán kết quả đầu ra

Kết quả:
Giải thích: hàm func có sử dùng dấu /, cho biết rằng các tham số đằng
trước phải truyền theo vị trí. Nhưng ta đã truyền ten, tuoi theo từ khóa
nên chương trình đã báo lỗi
h) Kết hợp truyền đối số chỉ theo từ khóa và chỉ theo vị trí
- Các tham số trước dấu / là positional-only arguments
- Các tham số sau dấu * là keyword-only arguments
- Các tham số nằm giữa / và * thì có thể truyền theo vị trí hay
từ khóa tùy ý

Ví dụ (gọi một bạn trong lớp trả lời). Cho chương trình, hãy
dự đoán kết quả của các lệnh gọi hàm
Chú thích:
+ Lệnh print(f"a={a}, b={b}, c={c}, d={d}") là f-string trong
Python.
+ Khi viết chuỗi với tiền tố f hoặc F, ta có thể chèn trực tiếp
biến và biểu thức Python vào trong dấu {}
+ Python sẽ tự động thay thế {} bằng giá trị biến hoặc biểu
thức tương ứng
Kết quả gọi hàm lần 1:
Kết quả gọi hàm lần 2:

Kết quả gọi hàm lần 3:

Giải thích:
- Các tham số positional-only là a, b
- Các tham số có thể truyền tùy ý là c
- Các tham số keyword-only là d
- Ở lần gọi hàm 1 (chạy đúng):
+ a, b được truyền theo vị trí (đúng loại)
+ d được truyền theo từ khóa (đúng loại)
- Ở lần gọi hàm 2 (chạy sai): a, b được truyền theo từ khóa (sai loại)
+ Ở lần gọi hàm 3 (chạy sai): d được truyền theo vị trí (sai loại)
C. Đối số *args và **kwargs
1. Đối số *args (Arbitrary Arguments)
a) Khái niệm
- *args được dùng để định nghĩa hàm có thể nhận số lượng
đối số không cố định (số lượng tùy ý)
- Tất cả các đối số truyền theo vị trí (positional arguments)
được gom thành một tuple
b) Cách hoạt động
- Khi khai báo hàm với *args, có thể truyền bao nhiêu đối số
cũng được
- Bên trong hàm, args sẽ là một tuple lưu trữ tất cả giá trị đó
- Lưu ý: *args chỉ nhận truyền đối số theo vị trí, không thể
truyền theo từ khóa
Ví dụ 1 (gọi một bạn trong lớp trả lời): Cho đoạn chương
trình. Dự đoán kết quả

Kết quả: (1, 2, 3)


Giải thích: args lưu tất cả giá trị đối số truyền vào, bất kể số
lượng nên kết quả là (1, 2, 3)
Ví dụ 2 (gọi một bạn trong lớp trả lời): Cho đoạn chương
trình. Dự đoán kết quả
Kết quả:
Giải thích: có hai tham số là s và *ten. Tham số s là “Chao”,
tham số ten là *args gồm {Thai, Chuong}. Khi gọi hàm, vòng
lặp for chạy sẽ in ra từng phần tử trong ten.
2. Đối số **kwargs (Arbitrary Keyword Arguments)
a) Khái niệm
- **kwargs được dùng để định nghĩa hàm có thể nhận số
lượng đối số theo từ khóa không cố định.
- Tất cả đối số truyền theo từ khóa sẽ được gom lại thành một
dictionary (là kiểu dữ liệu lưu trữ dưới dạng cặp key-value)
b) Cách hoạt động
- Khi khai báo hàm với **kwargs, có thể truyền bao nhiêu đối
số theo từ khóa cũng được
- Bên trong hàm, kwargs sẽ là một dictionary với key = tên
tham số, value = giá trị truyền vào
- Lưu ý: **kwargs chỉ nhận truyền đối số theo từ khóa, không
thể truyền theo vị trí
Ví dụ 1 (gọi một bạn trong lớp trả lời): Cho chương trình. Dự
đoán kết quả

Kết quả:

Giải thích: **kwargs là một dictionary lưu dưới dạng key-


value. Khi duyệt dictionary, sẽ in ra key : value.

Ví dụ 2 (gọi một bạn trong lớp trả lời): Cho chương trình. Dự
đoán kết quả

Kết quả:
Giải thích: hàm func có hai tham số s và **t (là kwargs). **t
là dictionary lưu dưới dạng key=value. Khi duyệt dictionary,
sẽ in ra key : value.
3. Kết hợp *args và **kwargs
- Sử dụng *args và **kwargs trong định nghĩa một hàm

Kết quả:

4. Mở rộng đối số (Unpacking Arguments)


- Unpacking Arguments là kỹ thuật trong Python cho phép
mở rộng một cấu trúc dữ liệu (list, tuple, dictionary) thành
các đối số riêng lẻ khi gọi hàm
a) Mở rộng lists với * (Unpacking Lists with *)
- Dùng dấu * để mở rộng list hoặc tuple thành các positional
arguments

Ví dụ (gọi một bạn trong lớp trả lời): Cho chương trình. Dự
đoán kết quả
Kết quả: 6
Giải thích:
+ Hàm func nhận 3 tham số a, b, c. Ta có một list so gồm các
phần tử là [1, 2, 3].
+ Khi gọi hàm func(*so), Python sẽ mở rộng list này thành 3
đối số theo vị trí lần lượt là 1, 2, 3.
+ Khi đó, cách gọi hàm này giống với cách gọi func(1, 2, 3).
Do đó, kết quả là 6
b) Mở rộng dictionaries với ** (Unpacking Dictionaries with
**)
- Dùng dấu ** để mở rộng dictionary thành các keyword
arguments

Ví dụ (gọi một bạn trong lớp trả lời): Cho chương trình. Dự
đoán kết quả
Kết quả:
Giải thích:
+ Hàm func nhận 3 tham số ten, tuoi, thanh_pho. Ta có một
dictionary d gồm các key-value là {"ten": "Thai", "tuoi": 17,
"thanh_pho": "Nha Trang"}
+ Khi gọi hàm func(**d), Python sẽ mở rộng dictionary này
thành 3 đối số theo từ khóa lần lượt là ten=”Thai”, tuoi=17,
thanh_pho=”Nha Trang”
+ Khi đó, cách gọi hàm này giống với cách gọi
func(ten=”Thai”, tuoi=17, thanh_pho=”Nha Trang”).
D. Phạm vi (Scope)
- Khái niệm: một biến chỉ tồn tại bên trong một khu vực mà
nó được khởi tạo. Đó được gọi là phạm vi (scope)
1. Phạm vi cục bộ (Local Scope)
- Khái niệm: một biến được khởi tạo bên trong một hàm thì sẽ
thuộc phạm vi trong hàm đó, và chỉ có thể truy cập biến đó
bên trong hàm

Ví dụ (gọi một bạn trong lớp trả lời): Cho đoạn chương trình.
Dự đoán kết quả của hai lệnh func() và print(x) ngoài hàm
Kết quả lệnh func(): 300
Kết quả lệnh print(x) ngoài hàm:

Giải thích:
+ Ở trong hàm func, ta khởi tạo x=300 trong hàm, nghĩa là
biến x chỉ tồn tại bên trong hàm func. Ta dùng lệnh print(x)
trong hàm func thì hoàn toàn hợp lệ
+ Ở ngoài hàm func, khi dùng lệnh print(x), sẽ gây ra lỗi
NameError, vì chương trình không tìm thấy biến nào là biến
x, điều này là do biến x chỉ tồn tại bên trong hàm func
+ Đó là phạm vi cục bộ (local scope) trong hàm func
2. Phạm vi toàn cục (Global Scope)
- Một biến được khởi tạo ở phần thân chương trình chính
được gọi là biến toàn cục (global variable) và nó sẽ thuộc
phạm vi toàn cục (global scope)
- Biến này tồn tại ở bất cứ đâu trong chương trình, bất cứ
phạm vi toàn cục hay cục bộ nào

Ví dụ (gọi một bạn trong lớp trả lời): Cho đoạn chương trình.
Dự đoán kết quả của hai lệnh func() và print(x) ngoài hàm

Kết quả: cả hai lệnh đều cho ra 300


Giải thích: x=300 được khởi tạo ở phần thân chương trình
chính, nên x là biến global. Do đó hai lệnh đều hợp lệ
3. Python phân biệt vị trí khởi tạo biến
- Khi khởi tạo hai biến trùng tên nhau, mỗi biến nằm ở ngoài
và ở trong một hàm, Python sẽ coi chúng là hai biến riêng
biệt
- Khi đó, một biến tồn tại ở phạm vi toàn cục (ngoài hàm),
một biến tồn tại ở phạm vi cục bộ (trong hàm)
Ví dụ (gọi một bạn trong lớp trả lời): Cho đoạn chương trình.
Dự đoán kết quả của hai lệnh func() và print(x) ngoài hàm

Kết quả lệnh func(): 200


Kết quả lệnh print(x) ngoài hàm: 300
Giải thích:
+ Trong hàm func, ta khởi tạo x=200, x này là cục bộ. Khi gọi
hàm, hàm sẽ in x cục bộ là 200
+ Ngoài hàm func, ta khởi tạo x=300, x này là toàn cục. Khi
chương trình chạy, chương trình in x toàn cục là 300
+ Điều này là hợp lệ vì Python sẽ coi hai biến đó là riêng biệt

4. Từ khóa “global” (Global Keyword)


a) Khái niệm
- global là từ khóa dùng bên trong hàm để cho biết ta muốn
sử dụng hoặc thay đổi biến ở phạm vi toàn cục (global
scope), thay vì tạo thêm một biến cục bộ mới
b) Cách dùng
- Mặc định, khi khởi tạo biến trong hàm, Python coi đó là biến
cục bộ
- Khi ta muốn truy cập và thay đổi biến toàn cục từ bên trong
hàm, ta phải khai báo global. Nếu không, chương trình sẽ
báo lỗi do biến cục bộ chưa được khởi tạo
- Lưu ý:
+ Ta phải khai báo biến ở phạm vi toàn cục rồi mới có thể xử
lí biến toàn cục đó bên trong hàm một cách hợp lệ.
+ Vì khi tìm thấy từ khóa global, chương trình sẽ tự động tìm
kiếm biến nào cùng tên được khởi tạo toàn cục

Ví dụ (gọi một bạn trong lớp trả lời): Cho đoạn chương trình.
Dự đoán kết quả

Kết quả:
Giải thích:
+ x được khai báo toàn cục, nhưng khi vào trong hàm func,
Python sẽ báo lỗi biến x cục bộ chưa được khởi tạo giá trị
+ Điều này là do Python phân biệt biến tùy theo vị trí, mặc
dù hai biến x cùng tên nhưng nếu một biến toàn cục và một
biến cục bộ, Python sẽ xem chúng là hai biến riêng biệt

* Cách khắc phục (có thể kêu một bạn lên viết): ta dùng từ
khóa global để có thể truy cập và xử lí biến x toàn cục bên
trong hàm

Kết quả: 15
Giải thích: ta đã dùng từ khóa global cho biến x trong hàm
func, điều này đồng nghĩa với việc ta đã có thể xử lí được
biến x toàn cục ở ngay bên trong hàm func
5. Từ khóa “nonlocal” (Nonlocal Keyword)
a) Khái niệm
- Từ khóa nonlocal được dùng trong nested function (hàm
lồng nhau) để ta có thể sử dụng hoặc thay đổi biến ở phạm
vi bao ngoài (enclosing scope), thay vì tạo biến cục bộ mới
b) Cách dùng
- Khi ta có một hàm nằm bên trong một hàm khác, biến ở
hàm ngoài (outer function) sẽ thuộc phạm vi bao ngoài
(enclosing scope)
- Nếu muốn thay đổi biến đó từ hàm bên trong (inner
function), ta phải dùng từ khóa nonlocal
- Nếu không dùng nonlocal, chương trình sẽ báo lỗi do biến
đó chưa được khởi tạo ở hàm bên trong

Ví dụ (gọi một bạn trong lớp trả lời): Cho đoạn chương trình.
Dự đoán kết quả
Kết quả:

Giải thích:
+ x được khởi tạo cục bộ ở trong hàm outer, điều này có
nghĩa là biến x này là biến cục bộ của hàm outer
+ Khi định nghĩa hàm inner và sử dụng biến x đó, Python sẽ
ngầm hiểu rằng biến x này là biến cục bộ của hàm inner
+ Do đó, chương trình sẽ báo lỗi rằng biến x cục bộ chưa
được khởi tạo

* Cách khắc phục (có thể kêu một bạn lên viết): ta dùng từ
khóa nonlocal để có thể sử dụng biến x ở phạm vi bao ngoài
(enclosing scope) hay ở trong hàm outer

Kết quả:
Giải thích: ta đã dùng từ khóa nonlocal cho biến x, nên ta có
thể sử dụng biến x của hàm outer trong phạm vi bao ngoài
6. Quy tắc LEGB (The LEGB Rule)
a) Khái niệm
- LEGB viết tắt của các từ
+ L (Local): Biến được khởi tạo bên trong hàm hiện tại (local
scope – phạm vi cục bộ)
+ E (Enclosing): Biến bên trong hàm bao ngoài (enclosing
function) khi có hàm lồng nhau
+ G (Global): Biến được khởi tạo toàn cục, bên ngoài mọi
hàm
+ B (Built-in): các tên có sẵn trong Python (như len, print,
range)
b) Nguyên lí hoạt động
- Python sẽ tìm kiếm biến theo thứ tự: Local -> Enclosing ->
Global -> Built-in

Ví dụ về LEGB (gọi một bạn nêu ra thứ tự in kết quả)


Kết quả:
Giải thích: theo quy tắc LEGB, chương trình sẽ in ra biến x
cục bộ trước, sau đó sẽ ra biến x ở hàm bao ngoài, rồi ra
biến x toàn cục
E. Hàm Decorator (Decorator Function)
1. Khái niệm
- Hàm decorator là một hàm đặc biệt dùng để bọc (wrap) một
hàm khác nhằm mở rộng hoặc thay đổi hành vi của hàm đó
mà không cần chỉnh sửa trực tiếp mã nguồn của hàm gốc
- Hàm để bọc một hàm khác thường gọi là hàm wrapper
2. Cách hoạt động
- Decorator nhận vào một hàm (hoặc class) làm đối số
- Trả về một hàm mới (hoặc đối tượng mới) có thêm chức
năng bổ sung
- Được khai báo bằng cú pháp @ten_decorator đặt ngay trên
hàm cần xử lí
3. Cách dùng
a) Hàm decorator cơ bản
- Cho một chương trình có sử dụng hàm decorator. Dự đoán
kết quả

Kết quả:
Giải thích:
+ Hàm decor nhận một hàm khác (func) làm đối số
+ Khi đặt @decor trực tiếp ở trên hàm func, hàm func sẽ
được “decorated” với hàm decor (decor là hàm decorator;
hàm func sẽ là hàm được decorate hay còn gọi là decorated
function)
+ Nó định nghĩa hàm wrapper() để bọc thêm logic trước và
sau khi gọi hàm func
+ Sau đó, decor trả về wrapper
+ Cú pháp:
@decor
def func(): print(“Thanh”)
tương tự như func = decor(func). Nghĩa là hàm func được
thay thế bằng phiên bản đã được bọc (wrapper)
+ Khi gọi hàm func, thực chất là gọi hàm wrapper. Trong
wrapper():
In Thai
Gọi hàm func() gốc -> In Thanh
In Chuong
b) Gọi hàm decorator nhiều lần (Multiple Decorator Calls)
- Hàm decorator có thể được gọi nhiều lần

Cho một chương trình có sử dụng hàm decorator. Dự đoán


kết quả
Kết quả:
Giải thích:
+ Hàm decor nhận một hàm khác (func) làm đối số
+ Nó định nghĩa hàm wrapper() để bọc thêm logic trước và
sau khi gọi hàm func
+ Sau đó, decor trả về wrapper
+ Cú pháp:
@decor
def func1(): print(“Chuong”)
tương tự như func1 = decor(func1). Nghĩa là hàm func1
được thay thế bằng phiên bản đã được bọc (wrapper)
+ Khi gọi hàm func1, thực chất là gọi hàm wrapper. Trong
wrapper():
In Thai
Gọi hàm func1() gốc -> In Chuong
In Thanh
c) Đối số của hàm đã được decorate (Arguments in Decorated
Function)
- Hàm có nhận đối số vẫn có thể được decorate, chỉ cần ta
truyền đối số vào hàm wrapper

Cho chương trình. Dự đoán kết quả


Kết quả:
Giải thích:
+ Ở đây, hàm wrapper có nhận tham số x, và trả về hàm
func(x) và upper() để chuyển kết quả thành chữ in hoa
+ Khi gọi func(“Thai”) -> thực chất gọi hàm wrapper(“Thai”)
+ Trong wrapper:
Gọi hàm gốc func(“Thai”) -> trả về “Hello Thai”
.upper() biến thành “HELLO THAI”
Sau đó trả về chuỗi “HELLO THAI”
d) Đối số *args và **kwargs trong hàm decorator
- Trong Python, khi viết decorator, ta thường không biết trước
được hàm được decorate (decorated function) sẽ có bao
nhiêu tham số, kiểu dữ liệu gì. Để đảm bảo decorator có thể
dùng cho mọi loại hàm, ta dùng đối số *args và **kwargs

Cho chương trình. Dự đoán kết quả


Kết quả:
Giải thích:
+ Hàm wrapper nhận hai tham số *args và **kwargs, thay
thế hàm func khi gọi hàm func
+ Khi gọi hàm func("Thai", "Vinh", "Chuong", d = "Thanh", e
= "Dung"), thực chất là gọi hàm wrapper("Thai", "Vinh",
"Chuong", d = "Thanh", e = "Dung")
+ Trong wrapper:
Gọi hàm gốc func -> trả về chuỗi "Hello Thai, Vinh,
Chuong, Thanh, Dung"
.lower() -> trả về chuỗi "hello thai, vinh, chuong,
thanh, dung"
e) Hàm decorator với đối số (Decorator with Arguments)
- Hàm decorator có thể nhận tham số bằng cách ta sẽ viết
một hàm trả về hàm decorator (nghĩa là một hàm lồng nhiều
hàm)

Cho chương trình. Dự đoán kết quả


Kết quả:
Giải thích:
+ Hàm tmp nhận tham số n. Hàm này sẽ chịu trách nhiệm
trong việc kiểm soát hành vi của hàm decor. Nó sẽ trả về
hàm decorator là hàm decor
+ Hàm decor nhận hàm gốc là hàm func làm tham số. Trả về
hàm wrapper
+ Hàm wrapper gọi hàm gốc là func, nếu n = 1 thì kết quả là
chữ thường, ngược lại là chữ hoa
+ Cú pháp @tmp(1)
Sẽ tương tự như lệnh: func = tmp(1)(func)
Lệnh này sẽ gọi tmp(1) -> trả về decor
Sau đó sẽ gọi decor(func) -> trả về wrapper
Cuối cùng, func sẽ được thay thế bằng wrapper
+ Khi gọi func():
Thực chất là gọi wrapper()
Trong wrapper(), gọi hàm gốc func() -> trả về “Hello
Thai”
Vì n = 1 -> chuyển thành “hello thai”
Sau đó ta được kết quả “hello thai”
f) Đa hàm decorator (Multiple Decorators)
- Ta có thể dùng nhiều hàm decorator cho cùng một hàm
bằng cách đặt lệnh gọi lên trên mỗi lệnh
- Các hàm được gọi theo thứ tự từ dưới lên, bắt đầu từ lệnh
gọi gần hàm nhất

Cho chương trình. Dự đoán kết quả

Kết quả:
Giải thích:
+ Hàm decor2 nhận hàm gốc func làm tham số. Trả về
wrapper() -> khi gọi sẽ chạy func() rồi thêm “Hello “ vào
trước kết quả
+ Hàm decor1 nhận kết quả của hàm decor2(func). Trả về
wrapper(), khi gọi sẽ chạy hàm đã được decorate bởi hàm
decor2, rồi chuyển toàn bộ sang chữ hoa
+ Thứ tự thực thi:
@decor1
@decor2
def func():
return "Thai"

Python sẽ gọi từ dưới lên, do đó lệnh gọi đó sẽ tương


đương với func = decor1(decor2(func)). Nghĩa là func
sẽ được decorate bởi hàm decor2 trước, rồi mới đến
decor1
+ Khi gọi func(), thực chất là gọi [Link](). Trong đó:
Gọi [Link]() -> chạy hàm gốc func() -> trả về
“Thai”
decor2 thêm “Hello “ -> “Hello Thai”
decor1 chuyển thành chữ hoa -> “HELLO THAI”
g) Bảo vệ metadata của hàm (Preserving Function Metadata)
- Hàm trong Python có metadata có thể truy cập bằng cách
dùng các thuộc tính là __name__ và __doc__
+ __name__: tên hàm
+ __doc__: chuỗi mô tả
- Khi ta dùng decorator để bọc một hàm, thường thì
metadata của hàm gốc sẽ bị mất
- Để khắc phục, có một hàm có sẵn là [Link] trong
thư viện functools giúp giữ lại metadata của hàm gốc
F. Hàm Generator (Generator Function)
1. Khái niệm
- Generator là hàm có thể tạm dừng hoặc tiếp tục lệnh thực
thi
- Khi gọi một hàm generator, nó trả về generator object, là
một iterator
- Code bên trong hàm sẽ chưa được thực thi, chỉ mới biên
dịch. Hàm chỉ thực thi lệnh khi ta duyệt hàm
2. Từ khóa yield (The yield Keyword)
- Từ khóa yield sẽ làm một hàm trở thành generator
- Khi gặp từ khóa yield, hàm sẽ tạm dừng thực thi, lưu lại
trạng thái hàm, và trả về một giá trị. Khi gọi hàm tiếp, hàm
sẽ tiếp tục thực thi tại chỗ dừng
- Không giống lệnh return (kết thúc hàm lập tức), yield có thể
làm cho hàm tạm dừng và có thể gọi hàm nhiều lần

Cho chương trình. Dự đoán kết quả


Kết quả:
Giải thích:
+ func là một hàm generator (do có từ khóa yield)
+ Bắt đầu i = 1 và chạy while i <= n
+ Mỗi lần lặp:
yield i -> trả về giá trị i cho vòng lặp bên ngoài
Sau đó tạm dừng hàm, chờ lần gọi tiếp theo
Khi tiếp tục, i tăng lên 1 và quay lại vòng lặp
+ Khi gọi func(5), không chạy toàn bộ hàm ngay lập tức. Thay
vào đó, trả về một generator object (một iterator đặc biệt)
+ Vòng lặp for i in func(5): mỗi lần lặp, generator chạy tiếp
từ chỗ dừng và sinh ra giá trị mới
3. Dùng hàm next() với Generator (Using next() with Generator)
- Ta có thể duyệt hàm generator thủ công bằng cách dùng
hàm next()
- Khi không còn giá trị để trả về, hàm sẽ báo lỗi exception là
StopIteration

Cho chương trình. Dự đoán kết quả

Kết quả:
+ Lần 1: 1
+ Lần 2: 2
+ Lần 3: StopIteration
Giải thích: 2 lần gọi đầu, có từ khóa yield nên hàm trả về 1
và 2. Nhưng đến lần gọi thứ 3, vì ko còn giá trị để trả về, hàm
báo lỗi StopIteration
4. Biểu thức Generator (Generator Expressions)
- Giống với tạo list (list comprehension) dùng dấu ngoặc
vuông, ta tạo generator bằng cách dùng biểu thức generator
(generator expression) dùng dấu ngoặc đơn thay vì ngoặc
vuông

Kết quả:
5. Các phương thức của hàm Generator (Generator Methods)
a) Phương thức send() (send() Method)
- Phương thức này cho phép ta gửi một giá trị cho hàm
generator
Kết quả:
b) Phương thức close() (close() Method)
- Phương thức này giúp dừng hàm generator
Kết quả:
6. Lợi ích và ứng dụng của hàm generator
- Tiết kiệm bộ nhớ: hàm generator
- Hỗ trợ tốt cho dữ liệu lớn
- Giúp tạo ra các dãy số vô hạn (như Fibonacci, Cấp số cộng,…)
mà không lo về tràn bộ nhớ

You might also like