Các Vấn Đề Thường Gặp trong Python - [11] Hiểu Sai Quy Tắc Phạm Vi Biến trong Python
By JoeVu, at: 12:39 Ngày 20 tháng 1 năm 2023
Thời gian đọc ước tính: __READING_TIME__ minutes
![Common Python Problems - [11] Misunderstanding Python scope rules](/media/filer_public_thumbnails/filer_public/74/0d/740dd188-b1e7-4c51-a2a7-67f9a5c782b9/python-mistake-scope-rules.jpeg__1500x900_q85_crop_subsampling-2_upscale.jpg)
![Common Python Problems - [11] Misunderstanding Python scope rules](/media/filer_public_thumbnails/filer_public/74/0d/740dd188-b1e7-4c51-a2a7-67f9a5c782b9/python-mistake-scope-rules.jpeg__400x240_q85_crop_subsampling-2_upscale.jpg)
Một trong những tính năng chính của Python là các quy tắc phạm vi, xác định cách các biến được truy cập và sửa đổi trong chương trình.
Tuy nhiên, các quy tắc phạm vi của Python có thể là nguồn gây nhầm lẫn cho nhiều nhà phát triển, dẫn đến lỗi và hành vi bất ngờ.
Trong bài viết này, chúng ta sẽ tìm hiểu một số hiểu lầm phổ biến về quy tắc phạm vi của Python và đưa ra các ví dụ về cách sử dụng chúng đúng cách.
1. Tổng quan về quy tắc phạm vi Python
Python có bốn loại phạm vi, còn được gọi là không gian tên
- Không gian tên tích hợp sẵn: chứa tất cả các hàm và hằng số tích hợp sẵn có sẵn trong Python
- Không gian tên toàn cục: chứa các biến được định nghĩa bên ngoài bất kỳ hàm hoặc lớp nào, và có thể truy cập được từ bất cứ đâu trong chương trình.
- Không gian tên bao quanh: chứa các biến được định nghĩa trong một hàm bên ngoài và có thể truy cập được trong một hàm bên trong
- Không gian tên cục bộ: chứa các biến được định nghĩa trong một hàm và chỉ có thể truy cập được trong hàm đó.
Khi một biến được truy cập hoặc sửa đổi, Python tìm kiếm các không gian tên này theo một thứ tự cụ thể để xác định giá trị của biến.
Python tuân theo quy tắc LEGB để tra cứu không gian tên: Local (Cục bộ), Enclosing (Bao quanh), Global (Toàn cục), và Built-in (Tích hợp sẵn). Khi một biến được tham chiếu trong một hàm, Python trước tiên tìm kiếm trong không gian tên cục bộ, sau đó là không gian tên bao quanh, tiếp theo là không gian tên toàn cục, và cuối cùng là không gian tên tích hợp sẵn. Nếu biến không được tìm thấy trong bất kỳ không gian tên nào trong số này, lỗi NameError sẽ được đưa ra.
2. Những hiểu lầm về quy tắc phạm vi Python
2.1 Hiểu lầm 1: Các biến toàn cục có thể được sửa đổi từ bên trong một hàm
Một hiểu lầm phổ biến về quy tắc phạm vi của Python là các biến toàn cục có thể được sửa đổi từ bên trong một hàm. Mặc dù các biến toàn cục có thể được truy cập từ bên trong một hàm, nhưng chúng không thể được sửa đổi trực tiếp. Thay vào đó, từ khóa global phải được sử dụng để chỉ ra rằng biến nên được xử lý như một biến toàn cục và được sửa đổi cho phù hợp.
Ví dụ, hãy xem xét đoạn mã sau:
x = 0
def increment():
x += 1
print(x)
increment()
Đoạn mã này sẽ đưa ra lỗi UnboundLocalError: local variable 'x' referenced before assignment. Lý do cho lỗi này là Python cho rằng x là một biến cục bộ vì nó đang được sửa đổi bên trong hàm. Để khắc phục điều này, chúng ta có thể thêm từ khóa global để chỉ ra rằng x là một biến toàn cục:
x = 0
def increment():
global x
x += 1
print(x)
increment()
Bây giờ, đầu ra sẽ là 1 vì x được xử lý đúng cách như một biến toàn cục và được tăng lên.
2.2 Hiểu lầm 2: Các biến trong vòng lặp là các biến cục bộ
Một hiểu lầm phổ biến khác về quy tắc phạm vi của Python là các biến được định nghĩa trong một vòng lặp là các biến cục bộ. Tuy nhiên, điều này không đúng. Các biến được định nghĩa trong một vòng lặp thực sự được coi là nằm trong không gian tên bao quanh và có thể truy cập được từ bên trong bất kỳ hàm lồng nào.
Ví dụ, hãy xem xét đoạn mã sau:
for i in range(5):
def print_i():
print(i)
print_i()
Đoạn mã này sẽ xuất ra 0 1 2 3 4 vì i được coi là nằm trong không gian tên bao quanh và có thể truy cập được từ bên trong hàm lồng print_i().
2.3 Hiểu lầm 3: Các biến được định nghĩa trong một hàm có thể truy cập được trong một hàm khác
Một hiểu lầm phổ biến thứ ba về quy tắc phạm vi của Python là các biến được định nghĩa trong một hàm có thể truy cập được trong một hàm khác. Tuy nhiên, điều này không đúng. Mỗi hàm đều có không gian tên cục bộ riêng của nó, và các biến được định nghĩa trong một hàm không thể truy cập được trong một hàm khác trừ khi chúng được truyền làm đối số.
Ví dụ, hãy xem xét đoạn mã sau:
def outer():
x = 1
def inner():
print(x)
inner()
outer()
Đoạn mã này sẽ in ra 1 một cách chính xác vì inner() có quyền truy cập vào không gian tên bao quanh, chứa x. Tuy nhiên, nếu chúng ta định nghĩa một hàm mới another_inner() và cố gắng truy cập x từ bên trong nó, chúng ta sẽ nhận được lỗi NameError:
def outer():
x = 1
def inner():
print(x)
def another_inner():
print(x)
inner()
another_inner()
outer()
Đoạn mã này sẽ đưa ra lỗi NameError: name 'x' is not defined vì another_inner() không có quyền truy cập vào không gian tên cục bộ của inner().
Để khắc phục điều này, chúng ta có thể truyền x làm đối số cho another_inner():
def outer():
x = 1
def inner():
print(x)
def another_inner(x):
print(x)
inner()
another_inner(x)
outer()
Bây giờ, đầu ra sẽ chính xác là 1 hai lần, một lần từ inner() và một lần từ another_inner().
3. Kết luận
Các quy tắc phạm vi của Python có thể là nguồn gây nhầm lẫn cho nhiều nhà phát triển, nhưng với sự hiểu biết vững chắc về cách chúng hoạt động, chúng ta có thể tránh được những hiểu lầm phổ biến và viết ra mã đáng tin cậy hơn. Bằng cách nhớ quy tắc LEGB và sử dụng đúng cách từ khóa global và đối số hàm, chúng ta có thể đảm bảo rằng các biến của chúng ta được truy cập và sửa đổi chính xác trong các chương trình của chúng ta.