10.1 Biến cục bộ của hàm
Trong Python, biến có thể được truy cập (có thể sử dụng) từ khi được tạo ra tới khi hết phạm vi của nó — thường nhất là hàm mà trong đó nó được khai báo. Nếu một biến được khai báo ngoài tất cả các hàm, nó được gọi là biến toàn cục.
Trong Python, phạm vi biến xác định ngữ cảnh mà trong đó biến có thể được sử dụng. Phạm vi giúp tránh xung đột tên và quản lý truy cập dữ liệu. Các loại phạm vi chính trong Python bao gồm:
Phạm vi cục bộ: Biến được tạo trong hàm tồn tại trong phạm vi cục bộ của hàm đó và chỉ có thể truy cập trong đó.
Phạm vi của hàm lồng: Nếu một hàm được định nghĩa trong một hàm khác, biến của nó chỉ có thể truy cập trong hàm lồng đó.
Phạm vi toàn cục: Biến, được định nghĩa ở cấp độ script hay module, được coi là toàn cục và có thể truy cập từ bất kỳ phần nào của mã trong cùng module đó.
Phạm vi nhúng: Đây là phạm vi đặc biệt bao gồm tất cả các đối tượng và hàm Python có sẵn mặc định (ví dụ: print() và len()).
Quy tắc LEGB
Để giải quyết các biến, Python sử dụng quy tắc LEGB, xác định thứ tự mà trình thông dịch tìm kiếm biến:
- L (Local) — Đầu tiên tìm kiếm trong phạm vi cục bộ.
- E (Enclosed) — Sau đó trong các phạm vi của tất cả các hàm lồng, từ gần nhất ra ngoài.
- G (Global) — Tiếp theo trong phạm vi toàn cục.
- B (Built-in) — Cuối cùng, trong phạm vi nhúng.
Ví dụ sử dụng
x = "global" # Biến toàn cục
def outer():
y = "outer local" # Biến cục bộ của hàm ngoài
def inner():
z = "inner local" # Biến cục bộ của hàm lồng
print(x) # Xuất "global"
print(y) # Xuất "outer local"
print(z) # Xuất "inner local"
inner()
outer()
Biến z
chỉ có thể truy cập trong hàm inner()
.
Biến y
có thể truy cập trong hàm outer()
và tất cả các hàm, được khai báo bên trong nó.
Biến x
có thể truy cập từ mọi nơi trong tệp hiện tại (module).
10.2 Truy cập biến toàn cục: global x
Một đặc điểm thú vị của ngôn ngữ Python là biến từ các phạm vi bên ngoài (phạm vi mà trong đó chứa phạm vi hiện tại) chỉ có thể được đọc.
Khi cố gắng ghi vào biến ngoài sẽ tạo ra biến cục bộ với cùng tên, và quyền truy cập tới biến ngoài sẽ bị mất.
Ví dụ:
x = 10
def change_global():
print(x) # Điều này sẽ gây ra lỗi, vì x sẽ được coi là biến cục bộ sau khi gán
x = 20 # Tại đây sẽ tạo ra biến cục bộ x
print(x) # Xuất 20 (truy cập biến cục bộ x)
change_global()
print(x) # Xuất 10
Ví dụ này không hoạt động và sẽ gây lỗi UnboundLocalError
, vì trình thông dịch Python đầu tiên thấy gán x = 20
và cho rằng x
là một biến cục bộ. Tuy nhiên, khi trình thông dịch đến dòng print(x)
, nó không tìm thấy biến cục bộ x
, vì biến này chưa được định nghĩa.
Cách làm này để đảm bảo an toàn, để biến cục bộ không vô tình thay đổi biến toàn cục.
Toán tử global
Nếu bạn cần cố tình thay đổi giá trị của biến toàn cục trong hàm, có thể sử dụng toán tử global
. Toán tử này cho phép chỉ định rõ ràng rằng thay đổi phải diễn ra ở biến toàn cục, không phải biến cục bộ.
Để thay đổi giá trị của biến toàn cục trong hàm, cần khai báo biến này ở đầu hàm bằng cách sử dụng global
. Điều này cho phép hàm có quyền ghi vào biến:
x = 10
def change_global():
global x # Khai báo x như biến toàn cục
print(x) # Xuất 10 (truy cập biến toàn cục x)
x = 20 # Tại đây gán giá trị mới cho biến toàn cục x
print(x) # Xuất 20 (truy cập biến toàn cục x)
change_global()
print(x) # Xuất 20
Sử dụng toán tử global
giúp tránh lỗi và quản lý biến toàn cục đúng cách.
Biến toàn cục có thể làm chương trình ít dự đoán hơn và khó hiểu hơn, vì giá trị của chúng có thể thay đổi ở bất kỳ đâu trong chương trình. Điều này đặc biệt quan trọng nếu chương trình lớn và được phát triển bởi một nhóm lập trình viên.
Dù đôi khi việc sử dụng biến toàn cục là không thể tránh khỏi, cố gắng giảm thiểu chúng. Thay vì biến toàn cục, hãy cân nhắc sử dụng tham số hàm, giá trị trả về và các lớp để lưu trữ trạng thái.
Sử dụng biến toàn cục có thể dẫn đến các hiệu ứng phụ không mong muốn, làm khó khăn việc gỡ lỗi và kiểm thử mã, cũng như giảm khả năng sử dụng lại mã. Do đó, nên sử dụng biến toàn cục một cách cẩn thận và chỉ khi thật sự cần thiết.
10.3 Truy cập biến không phải cục bộ: nonlocal
Ngoài biến toàn cục và cục bộ, trong Python còn có các biến từ các phạm vi trung gian. Ví dụ, khi một hàm chứa trong một hàm khác. Để làm việc với những biến như vậy, chúng ta sử dụng toán tử nonlocal
.
Toán tử nonlocal
cho phép làm việc với biến trong các hàm lồng, thay đổi giá trị của chúng trong phạm vi gần nhất, loại trừ các biến toàn cục.
Toán tử nonlocal
giúp tránh tạo ra biến cục bộ mới trong hàm lồng, khi cần thay đổi biến đã được định nghĩa trong hàm ngoài. Nếu không sử dụng nonlocal
, thay đổi chỉ ảnh hưởng đến biến cục bộ của hàm bên trong, không ảnh hưởng đến biến trong hàm bên ngoài.
Ví dụ:
def outer():
count = 0
def inner():
nonlocal count
count += 1
return count
return inner
counter = outer()
print(counter()) # Xuất 1
Đây là một ví dụ thực tế hơn về sử dụng nonlocal
để tạo một bộ đếm:
def create_counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
counter = create_counter()
print(counter()) # Xuất 1
print(counter()) # Xuất 2
print(counter()) # Xuất 3
Ví dụ này thể hiện cách nonlocal
có thể được sử dụng trong các tình huống thực tế để tạo ra một hàm duy trì trạng thái của nó giữa các lần gọi.
GO TO FULL VERSION