Variable Scope trong Python là khu vực mà một biến có thể được truy cập và sử dụng. Việc hiểu rõ về phạm vi của biến giúp lập trình viên quản lý dữ liệu một cách hiệu quả hơn. Bài viết này sẽ cung cấp cái nhìn tổng quan về phạm vi biến, bắt đầu bằng việc định nghĩa khái niệm cơ bản và sau đó tìm hiểu chi tiết về một số loại phạm vi phổ biến trong Python.
Variable Scope trong Python là gì?
Phạm vi biến (variable scope) trong Python được hiểu là khu vực hoặc vùng cụ thể mà ở đó, biến có thể được truy cập và sử dụng. Phạm vi của một biến phụ thuộc vào vị trí và cách thức biến đó được định nghĩa. Hiểu một cách đơn giản, phạm vi biến xác định biến ở đâu và có thể “nhìn thấy” ở đâu trong chương trình của bạn.
Trong Python, một biến có thể có phạm vi toàn cục hoặc phạm vi cục bộ, và thậm chí một loại phạm vi trung gian là phạm vi bao ngoài.
Các loại variable scope trong Python
Dựa trên phạm vi, các biến trong Python được phân loại thành 3 loại chính:
- Biến cục bộ (Local Variables): Các biến này chỉ có thể truy cập và sử dụng bên trong một khối mã cụ thể, như bên trong một hàm.
- Biến toàn cục (Global Variables): Các biến này có thể được truy cập và sử dụng từ bất kỳ đâu trong chương trình.
- Biến bao ngoài (Nonlocal Variables): Các biến này nằm trong phạm vi của các hàm lồng nhau và có thể truy cập được ở các hàm bên trong.
Biến cục bộ (Local Variables)
Biến cục bộ (Local Variable) là một biến được khai báo bên trong một hàm hoặc một khối mã (block of code) cụ thể. Điều quan trọng cần nhớ là phạm vi của biến cục bộ bị giới hạn, có nghĩa là chúng chỉ có thể được sử dụng bên trong chính hàm hoặc khối mã mà biến đó được định nghĩa.
Hay nói cách khác, biến cục bộ chỉ hoạt động bên trong phạm vi mà nó được khai báo; nếu bạn cố gắng truy cập một biến cục bộ ở bên ngoài hàm, chương trình sẽ báo lỗi. Một điểm đặc biệt khác là bạn hoàn toàn có thể có nhiều biến cục bộ khác nhau cùng tên, miễn là các biến đó nằm trong các hàm hoặc các khối mã khác nhau.
Ví dụ
Ví dụ sau đây minh họa phạm vi hoạt động của biến cục bộ trong Python.
def tinh_dien_tich_hinh_chu_nhat(chieu_dai, chieu_rong):
dien_tich = chieu_dai * chieu_rong
thong_bao = "Diện tích hình chữ nhật là: "
print (thong_bao, dien_tich)
return dien_tich
# Gọi hàm và in kết quả
print ("Kết quả:", tinh_dien_tich_hinh_chu_nhat(5, 10))
Ở đoạn mã trên, chúng ta sử dụng các biến dien_tich, thong_bao trong hàm tinh_dien_tich_hinh_chu_nhat, kết quả là chương trình chạy được. Chương trình này cho ra kết quả như sau:
Diện tích hình chữ nhật là: 50
Kết quả: 50
Giải thích code:
def tinh_dien_tich_hinh_chu_nhat(chieu_dai, chieu_rong):
: Câu lệnh này định nghĩa một hàm tên làtinh_dien_tich_hinh_chu_nhat
với hai tham số đầu vào làchieu_dai
vàchieu_rong
(chiều dài và chiều rộng của hình chữ nhật).dien_tich = chieu_dai * chieu_rong
: Biếndien_tich
được định nghĩa và tính toán giá trị dựa trênchieu_dai
vàchieu_rong
. Đây là một ví dụ về biến cục bộ, có phạm vi trong hàmtinh_dien_tich_hinh_chu_nhat
.thong_bao = "Diện tích hình chữ nhật là: "
: Một biến cục bộ khác làthong_bao
, chứa chuỗi ký tự cần in ra, cũng có phạm vi hoạt động chỉ bên trong hàm.print (thong_bao, dien_tich)
: Câu lệnh này in thông báo và kết quả tính toán ra màn hình.return dien_tich
: Hàm trả về giá trị của biếndien_tich
khi kết thúc hàm.print ("Kết quả:", tinh_dien_tich_hinh_chu_nhat(5, 10))
: Dòng này gọi hàmtinh_dien_tich_hinh_chu_nhat
với giá trịchieu_dai
bằng 5, vàchieu_rong
bằng 10 sau đó in ra kết quả trả về của hàm này.
Biến toàn cục (Global Variables)
Biến toàn cục (Global Variable) là một loại biến có thể được truy cập từ bất kỳ đâu trong chương trình. Điểm khác biệt của biến toàn cục so với biến cục bộ nằm ở chỗ: biến toàn cục được định nghĩa bên ngoài bất kỳ hàm hoặc khối mã nào. Chính vì thế, biến toàn cục không thuộc riêng về một hàm hoặc một khối mã cụ thể nào cả. Bạn có thể hình dung rằng biến toàn cục giống như một “tài sản chung” mà mọi nơi trong chương trình đều có thể sử dụng đến.
Ví dụ
Ví dụ dưới đây sẽ cho bạn thấy các mà biến toàn cục hoạt động:
# Các biến toàn cục
vat_rate = 0.1 # Thuế VAT là 10%
company_name = "Cửa Hàng ABC"
def tinh_gia_san_pham(gia_goc):
# Sử dụng biến toàn cục bên trong hàm
gia_vat = gia_goc * (1 + vat_rate)
print (f"Tên công ty: {company_name}")
print(f"Giá sản phẩm sau VAT: {gia_vat}")
return gia_vat
# Gọi hàm và in kết quả
tinh_gia_san_pham(100)
print (f"Giá VAT hiện tại: {vat_rate}")
Đoạn mã trên cho thấy cách ta scó thể dùng biến toàn cục ở trong cũng như ngoài phạm vi một hàm, chương trình sẽ cho kết quả như sau:
Tên công ty: Cửa Hàng ABC
Giá sản phẩm sau VAT: 110.0
Giá VAT hiện tại: 0.1
Giải thích code:
vat_rate = 0.1
vàcompany_name = "Cửa Hàng ABC"
: Hai câu lệnh này định nghĩa hai biến toàn cục:vat_rate
(tỷ lệ VAT) vàcompany_name
(tên công ty). Chúng được khai báo ở bên ngoài bất kỳ hàm nào.def tinh_gia_san_pham(gia_goc):
: Câu lệnh này định nghĩa hàmtinh_gia_san_pham
với một tham số đầu vào làgia_goc
(giá gốc của sản phẩm).gia_vat = gia_goc * (1 + vat_rate)
: Bên trong hàm, chúng ta sử dụng biến toàn cụcvat_rate
để tính giá sản phẩm sau khi đã cộng thuế VAT. Biếngia_vat
là một biến cục bộ của hàm.print(f"Tên công ty: {company_name}")
: câu lệnh này sử dụng biến toàn cụccompany_name
để in tên công ty.print(f"Giá sản phẩm sau VAT: {gia_vat}")
: in ra giá sau khi đã tính toán thuế.return gia_vat
: Trả về giá sản phẩm đã tính thuế.tinh_gia_san_pham(100)
: Câu lệnh này gọi hàmtinh_gia_san_pham
với giá gốc là 100, từ đó tính và in giá sau thuế.print(f"Giá VAT hiện tại: {vat_rate}")
: Câu lệnh này truy cập biến toàn cụcvat_rate
ở bên ngoài hàm để in giá trị VAT hiện tại.
Biến không cục bộ (Nonlocal Variables)
Trong Python, một biến không cục bộ (nonlocal variable) là một biến không được định nghĩa trong phạm vi cục bộ của hàm hiện tại, cũng như không phải là biến toàn cục. Nói cách khác, chúng thường xuất hiện trong các hàm được định nghĩa lồng nhau bên trong một hàm khác.
Vậy, khi nào thì chúng ta dùng đến biến nonlocal? Chúng ta dùng biến nonlocal khi muốn truy cập và thay đổi giá trị của một biến ở phạm vi ngoài (bao quanh), nhưng không phải ở phạm vi toàn cục.
Ví dụ
Hãy xem xét ví dụ sau đây để hiểu rõ hơn về cách biến không cục bộ hoạt động:
def tinh_tien_thue_va_thu_nhap():
thue_suat = 0.1
thu_nhap = 1000
# Hàm lồng nhau (nested function)
def tinh_thue():
nonlocal thu_nhap
nonlocal thue_suat
thue = thu_nhap * thue_suat
thu_nhap += 500 #Tăng thêm thu nhập sau khi có thêm nguồn thu khác
print ("Thuế:", thue)
print("Thu nhập hiện tại:", thu_nhap)
return thue
print("Kết quả tiền thuế:", tinh_thue())
tinh_tien_thue_va_thu_nhap()
Khi chạy đoạn mã trên, bạn sẽ nhận được kết quả như sau:
Thuế: 100.0
Thu nhập hiện tại: 1500
Kết quả tiền thuế: 100.0
Giải thích code:
def tinh_tien_thue_va_thu_nhap():
: Hàm này định nghĩa một tình huống tính toán tiền thuế và thu nhập, có chứa một hàm con bên trong.thue_suat = 0.1, thu_nhap = 1000
: Biếnthue_suat
vàthu_nhap
được khởi tạo trong hàmtinh_tien_thue_va_thu_nhap
.def tinh_thue():
: Hàm này được định nghĩa bên trong hàmtinh_tien_thue_va_thu_nhap
, nó là một hàm lồng nhau.nonlocal thu_nhap, nonlocal thue_suat
: Câu lệnhnonlocal
khai báo rằngthu_nhap
vàthue_suat
là biến nonlocal, tức chúng không phải là biến cục bộ củatinh_thue
mà là biến được định nghĩa ở phạm vi ngoài (trongtinh_tien_thue_va_thu_nhap
). Điều này cho phép hàm con thay đổi các biếnthu_nhap
vàthue_suat
ở phạm vi hàm ngoài.thue = thu_nhap * thue_suat
: Tính tiền thuế bằng cách nhân thu nhập với thuế suất.thu_nhap += 500
: Cập nhật thu nhập bằng cách tăng thêm 500.print ("Thuế:", thue)
: In số tiền thuế tính được.print("Thu nhập hiện tại:", thu_nhap)
: In thu nhập hiện tại sau khi có thêm nguồn thu.return thue
: Trả về giá trị thuế vừa tính.print("Kết quả tiền thuế:", tinh_thue())
: Gọi hàmtinh_thue
và in ra giá trị thuế tính được từ hàm con.
Namespace và variable scope trong Python
Trong Python, không gian tên (namespace) có thể hiểu đơn giản là một tập hợp các định danh (identifier), bao gồm tên biến, tên hàm, tên lớp, và nhiều thứ khác nữa. Nói một cách dễ hiểu hơn, không gian tên giống như một “danh bạ” của chương trình, giúp Python quản lý các tên gọi khác nhau, và tránh xảy ra xung đột tên (khi hai biến có cùng tên) trong quá trình thực thi. Nhờ có không gian tên, mỗi định danh có một vùng hoạt động riêng, và Python biết chính xác bạn đang muốn tham chiếu đến định danh nào.
Python cung cấp một số loại không gian tên chính:
- Không gian tên Built-in: Chứa các hàm dựng sẵn (ví dụ:
print()
,len()
,…), và các ngoại lệ dựng sẵn của Python. Không gian tên này được nạp vào bộ nhớ ngay khi trình thông dịch Python (Python interpreter) khởi động và tồn tại cho đến khi trình thông dịch kết thúc. - Không gian tên Global: Chứa tất cả các tên (biến, hàm,…) được khai báo ở phạm vi chính của chương trình. Không gian tên global này tồn tại trong bộ nhớ cho đến khi chương trình hoàn tất.
- Không gian tên Local: Chứa các tên được khai báo bên trong một hàm. Các tên trong không gian tên local này chỉ tồn tại và có thể truy cập trong thời gian hàm được thực thi. Khi hàm kết thúc, không gian tên local cũng biến mất.
Các không gian tên này được sắp xếp lồng ghép vào nhau, theo một trật tự nhất định. Sơ đồ dưới đây mô tả mối quan hệ giữa các không gian tên này:
Một điều quan trọng cần nhớ: Vòng đời (hay “thời gian tồn tại”) của một biến được giới hạn trong không gian tên mà nó được định nghĩa. Điều này đồng nghĩa với việc, chúng ta không thể truy cập một biến ở không gian tên bên trong, từ một không gian tên bên ngoài. (ví dụ, một biến được định nghĩa trong một hàm sẽ không thể sử dụng trực tiếp ở bên ngoài hàm đó).
Hàm globals() trong Python
Trong thư viện chuẩn của Python, chúng ta có một hàm dựng sẵn (built-in function) rất hữu ích, đó là hàm globals(). Hàm globals() có chức năng trả về một từ điển (dictionary), mà từ điển này chứa tất cả các ký hiệu (symbols), ví dụ như tên biến, tên hàm, tên lớp,… hiện có trong không gian tên toàn cục (global namespace) của chương trình. Nói một cách dễ hiểu, globals() giúp chúng ta xem được tất cả những “tên” đang được Python sử dụng tại phạm vi toàn cục.
Bạn có thể chạy trực tiếp hàm globals()
ngay từ trình thông dịch Python (Python prompt) để quan sát kết quả:
>>> globals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>}
Khi bạn gọi hàm globals()
mà chưa định nghĩa biến nào, Python sẽ trả về một từ điển chứa thông tin về các biến môi trường và module (thư viện) được tải mặc định của Python.
Như bạn thấy ở trên, từ điển kết quả trả về có chứa __builtins__
, là một module (thư viện) chứa các định nghĩa của tất cả các hàm dựng sẵn và các ngoại lệ (exception) dựng sẵn. Thư viện này sẽ được tự động tải khi trình thông dịch Python khởi chạy, đảm bảo bạn có thể sử dụng các hàm quen thuộc như print()
, len()
,… ở bất cứ đâu trong code của mình.
Ví dụ
Hãy cùng xem xét đoạn code Python dưới đây. Đoạn code này chứa một số biến ở phạm vi global, và một hàm, bên trong hàm đó lại có thêm một vài biến khác:
website = 'TechBlog'
nam_phat_hanh = 2023
da_hoat_dong = True
def tinh_tong(x,y):
a = x + 10
b = y + 20
print ("Biến cục bộ bên trong hàm:", a,b)
return a + b
print (globals())
print (tinh_tong(1,2))
Khi thực thi đoạn code trên và sử dụng hàm globals(), bạn sẽ thấy một kết quả dạng dictionary (từ điển) tương tự như sau:
{‘__name__’: ‘__main__’, ‘__doc__’: None, ‘__package__’: None, ‘__loader__’: <_frozen_importlib_external.SourceFileLoader object at 0x000001CB40F56500>, ‘__spec__’: None, ‘__annotations__’: {}, ‘__builtins__’:
Biến cục bộ bên trong hàm: 11 22
33
Giải thích kết quả trên:
globals()
: Hàmglobals()
trả về một từ điển (dictionary), trong đó chứa tất cả các biến và hàm đang tồn tại ở không gian tên global.- Các biến Global: Bạn có thể thấy các biến như
'website'
,'nam_phat_hanh'
,'da_hoat_dong'
(và giá trị tương ứng của chúng), và cả tên của hàmtinh_tong
, cùng với vị trí của hàm trong bộ nhớ được liệt kê trong kết quả từ hàmglobals()
. Những biến này được khai báo ở phạm vi global của chương trình, nên chúng được lưu trữ ở không gian tên global. - Các biến local: Điều quan trọng cần chú ý là, các biến
a
,b
được khai báo bên trong hàmtinh_tong
, không được xuất hiện ở kết quả củaglobals()
. Vì các biến này có phạm vi cục bộ, chỉ thuộc về không gian tên của hàmtinh_tong
, do đó không thể được “nhìn thấy” khi truy cập từ bên ngoài hàm.
Giải thích code chi tiết:
website = 'TechBlog', nam_phat_hanh = 2023, da_hoat_dong = True
: Các biến này được định nghĩa ở phạm vi global của chương trình.def tinh_tong(x,y):
: Câu lệnh này định nghĩa hàm có têntinh_tong
và hai tham số làx
,y
.a = x + 10, b = y + 20
: Các biếna
,b
được định nghĩa bên trong hàm, và do đó là biến cục bộ. Chúng được dùng để tính toán.print ("Biến cục bộ bên trong hàm:", a,b)
: Câu lệnh này in các biếna
,b
trước khi trả về kết quả.print (globals())
: In các biến global ra.print (tinh_tong(1,2))
: Gọi hàmtinh_tong
vớix
là1
vày
là2
, sau đó in ra kết quả trả về từ hàm.
Hàm locals() trong Python
Hàm locals()
là một hàm dựng sẵn của Python (có nghĩa là nó đã có sẵn, bạn không cần phải “nhập” nó) và được dùng để xem các biến hiện có trong không gian tên cục bộ của một hàm. Hàm này trả về một dictionary (từ điển), với các key (khóa) là tên các biến cục bộ, và value (giá trị) là giá trị của các biến đó.
Ví dụ
Ví dụ dưới đây sẽ minh họa cho cách hoạt động của hàm locals()
trong Python:
ten_bai_giang = 'Python cơ bản'
diem_so = 70
ket_qua = True
def tinh_tong_va_hien_thi():
x = 5
y = 15
tong = x + y
print ("globals():", globals())
print ("locals():", locals())
return tong
tinh_tong_va_hien_thi()
Kết quả in ra cho thấy locals()
trả về một dictionary chứa các biến và giá trị của chúng trong không gian tên cục bộ của hàm:
globals(): {‘__name__’: ‘__main__’, ‘__doc__’: None, ‘__package__’: None, ‘__loader__’: <_frozen_importlib_external.SourceFileLoader object at 0x...>, ‘__spec__’: None, ‘__annotations__’: {}, ‘__builtins__’:
locals(): {‘x’: 5, ‘y’: 15, ‘tong’: 20}
Vì cả globals()
và locals()
đều trả về một dictionary, nên chúng ta có thể dễ dàng lấy giá trị của một biến trong không gian tên tương ứng bằng phương thức get()
hoặc dùng toán tử []
để truy cập thông qua key (tên của biến)
print(globals()['ten_bai_giang']) # In ra Python cơ bản
print(locals().get('x')) # In ra 5
Giải thích code:
ten_bai_giang = 'Python cơ bản', diem_so = 70, ket_qua = True
: Các biến này được định nghĩa ở cấp độ toàn cục, chúng thuộc global namespace.def tinh_tong_va_hien_thi():
: Định nghĩa một hàmtinh_tong_va_hien_thi
, trong hàm này, chúng ta sẽ xem xét về biến cục bộ.x = 5, y = 15, tong = x + y
: Các biến này được khai báo và gán giá trị trong phạm vi của hàm, chúng là các biến cục bộ.print ("globals():", globals())
: Dòng này in ra tất cả các biến và giá trị tương ứng trong không gian tên global của chương trình.print ("locals():", locals())
: Dòng này in ra tất cả các biến cục bộ trong phạm vi của hàmtinh_tong_va_hien_thi
.print(globals()['ten_bai_giang'])
: Ví dụ cách truy cập vào một biến toàn cục.print(locals().get('x'))
: Ví dụ cách truy cập vào biến cục bộ bằngget()
.
Xung đôt tên (Namespace Conflict) trong Python
Điều gì sẽ xảy ra nếu chúng ta có hai biến trùng tên nhau, một được khai báo ở phạm vi global (toàn cục), và một được khai báo ở phạm vi local (cục bộ)? Trong trường hợp này, trình thông dịch Python sẽ ưu tiên sử dụng biến nằm trong không gian tên local khi có xung đột xảy ra. Tức là khi gặp một tên biến giống nhau, Python sẽ “tìm” biến cục bộ trước, nếu có sẽ sử dụng biến đó, và nếu không, nó mới tìm tới biến toàn cục.
Ví dụ 1
Hãy xem qua ví dụ sau để hiểu rõ hơn về cách Python xử lý xung đột tên:
diem_so = 80 # đây là biến toàn cục (global variable)
def in_diem_so():
diem_so = 90 # đây là biến cục bộ (local variable)
print ("Điểm số trong hàm:", diem_so)
in_diem_so()
print ("Điểm số bên ngoài hàm:", diem_so) # in ra giá trị của biến global
Kết quả khi chạy đoạn code này như sau:
Điểm số trong hàm: 90
Điểm số bên ngoài hàm: 80
Giải thích code:
diem_so = 80
: Dòng này khai báo một biến global có tên làdiem_so
và gán giá trị ban đầu bằng 80. Biến này có phạm vi hoạt động trong toàn bộ chương trình.def in_diem_so()
: Câu lệnh này định nghĩa một hàm có tên làin_diem_so
.diem_so = 90
: Bên trong hàmin_diem_so
, một biến local có cùng têndiem_so
lại được khai báo và gán giá trị là 90.print ("Điểm số trong hàm:", diem_so)
: Câu lệnh này in ra giá trị của biếndiem_so
trong phạm vi hàm (tức là biến local với giá trị 90).in_diem_so()
: Dòng này gọi hàmin_diem_so
, khiến các câu lệnh bên trong hàm được thực thi.print ("Điểm số bên ngoài hàm:", diem_so)
: Sau khi hàmin_diem_so
kết thúc, dòng lệnh này in ra giá trị của biếndiem_so
ở bên ngoài hàm (tức là biến global với giá trị 80).
Ví dụ về lỗi UnboundLocalError
Ở ví dụ này chúng ta sẽ có một tình huống, ta cố gắng thay đổi giá trị của một biến toàn cục (global variable) từ bên trong một hàm. Khi bạn làm điều này, Python sẽ báo lỗi UnboundLocalError
, như trong ví dụ sau đây:
# Đây là một biến toàn cục
diem_so = 50
def cap_nhat_diem():
diem_so = diem_so + 20
print(diem_so)
cap_nhat_diem()
# In giá trị toàn cục
print(diem_so)
Khi chạy đoạn code trên, sẽ sinh ra thông báo lỗi như sau:
diem_so = diem_so + 20
^^^^^^^^
UnboundLocalError: cannot access local variable ‘diem_so’ where it is not associated with a value
Giải thích code và lỗi:
diem_so = 50
: Đây là một biến toàn cục được khai báo bên ngoài hàm và có tên làdiem_so
(điểm số) với giá trị khởi tạo là 50.def cap_nhat_diem():
: Câu lệnh này định nghĩa một hàm tên làcap_nhat_diem
(cập nhật điểm). Mục đích của hàm này là thay đổi giá trị biếndiem_so
.diem_so = diem_so + 20
: Đây là dòng lệnh gây ra lỗi. Bên trong hàmcap_nhat_diem
, khi Python gặp lệnh gán này, Python sẽ coidiem_so
là một biến cục bộ (vì ta đang cố gán một giá trị mới chodiem_so
). Tuy nhiên, vì đây là lần đầu tiên Python thấydiem_so
bên trong hàm và nó chưa được gán giá trị khởi tạo trong hàm này (trước lệnh gán đó), nên Python báo lỗiUnboundLocalError
. Có nghĩa là “không thể truy cập biến cục bộdiem_so
khi biến này chưa được liên kết với một giá trị nào cả”.
Nói một cách đơn giản, Python hiểu rằng bạn đang muốn tạo ra một biến cục bộ mới có cùng tên làdiem_so
, nhưng bạn lại dùng biến cục bộ này (ở vế phải của phép gán) trước khi bạn tạo ra nó (gán giá trị khởi tạo cho nó).print(diem_so)
: Lệnh in giá trị của biếndiem_so
, biến cục bộ của hàm sau khi ta đã cố gắng thao tác. (Lưu ý, vì lỗiUnboundLocalError
đã xảy ra, câu lệnh này không bao giờ được chạy tới)print(diem_so)
(ngoài hàm): Lệnh này in giá trị của biến toàn cụcdiem_so
ở bên ngoài hàm (nếu hàm không bị lỗi). Trong trường hợp này, kết quả nếu không có lỗi sẽ in ra là 50 (vì biến toàn cục không bị thay đổi)
Ví dụ về cách thay đổi biến toàn cục
Để có thể chỉnh sửa một biến toàn cục (global variable) từ bên trong một hàm, bạn có thể lựa chọn một trong hai cách sau đây:
- Sử dụng cú pháp dictionary để truy cập vào không gian tên global.
- Sử dụng từ khóa global để khai báo biến đó là biến toàn cục trước khi thay đổi giá trị.
Chúng ta sẽ xem xét một ví dụ minh họa cả hai cách này:
var1 = 50 # Đây là một biến toàn cục
var2 = 60 # Đây là một biến toàn cục
def thay_doi_bien_toan_cuc():
"Thay đổi giá trị của biến toàn cục"
globals()['var1'] = globals()['var1'] + 10 # Sử dụng dictionary để thay đổi
global var2 # Khai báo biến var2 là biến toàn cục
var2 = var2 + 20 # Thay đổi giá trị biến var2
thay_doi_bien_toan_cuc()
print ("var1:", var1, "var2:", var2) # In ra biến toàn cục sau khi thay đổi
Khi đoạn code trên, kết quả đầu ra sẽ là:
var1: 60 var2: 80
Giải thích code:
var1 = 50
vàvar2 = 60
: Hai dòng này khai báo hai biến toàn cục có tên làvar1
vàvar2
, với các giá trị khởi tạo lần lượt là 50 và 60.def thay_doi_bien_toan_cuc():
: Câu lệnh này định nghĩa một hàm tên làthay_doi_bien_toan_cuc
(thay đổi biến toàn cục). Mục đích của hàm này là thay đổi giá trị của hai biến toàn cụcvar1
vàvar2
.globals()['var1'] = globals()['var1'] + 10
:globals()
: là một hàm dựng sẵn trong Python, trả về một dictionary đại diện cho không gian tên global.- Chúng ta sử dụng
globals()['var1']
để truy cập vào biến toàn cục có tênvar1
như là một phần tử trong dictionary (thông qua key của dictionary), sau đó tăng giá trị biếnvar1
này lên 10. Đây là cách đầu tiên để truy cập và thay đổi biến toàn cục từ bên trong hàm.
global var2
:- Câu lệnh này khai báo cho Python biết rằng chúng ta đang muốn sử dụng và chỉnh sửa biến toàn cục có tên
var2
bên trong hàm này. - Nếu không có khai báo này, khi sử dụng
var2
, Python sẽ tự động tạo một biến cục bộ trong hàm có tên làvar2
.
- Câu lệnh này khai báo cho Python biết rằng chúng ta đang muốn sử dụng và chỉnh sửa biến toàn cục có tên
var2 = var2 + 20
: Sau khi đã khai báovar2
là một biến toàn cục vớiglobal var2
, giờ đây chúng ta có thể thực hiện thay đổi giá trị của nó. Chúng ta đã tăng giá trị củavar2
lên 20.thay_doi_bien_toan_cuc()
: Câu lệnh này gọi hàmthay_doi_bien_toan_cuc
, dẫn đến việc hai biếnvar1
vàvar2
sẽ bị thay đổi giá trị.print ("var1:", var1, "var2:", var2)
: Cuối cùng, lệnh này in ra giá trị đã thay đổi củavar1
vàvar2
. Kết quả là 60 và 80, thể hiện rằng các thay đổi bên trong hàm đã có tác dụng đến các biến toàn cục.
Ví dụ về lỗi NameError
Ở ví dụ này, bạn hãy xem điều gì sẽ xảy ra khi bạn cố gắng truy cập một biến cục bộ từ bên ngoài hàm (ở phạm vi toàn cục). Python sẽ báo lỗi NameError (Lỗi Tên) vì biến cục bộ không thể được truy cập từ bên ngoài phạm vi của nó.
x = 10 # Đây là một biến toàn cục
y = 20 # Đây là một biến toàn cục
def tinh_tong(a, b):
ket_qua = a + b
print ("ket_qua là biến cục bộ:", ket_qua)
tinh_tong(x,y)
print(ket_qua) # Dòng này gây ra NameError
Đoạn code trên sẽ sinh ra thông báo lỗi như sau:
ket_qua là biến cục bộ: 30
Traceback (most recent call last):
File “main.py”, line 9, in
print(ket_qua) # Dòng này gây ra NameError
^^^^^^^
NameError: name ‘ket_qua’ is not defined
Giải thích code và lỗi:
x = 10, y = 20
: Đây là hai biến toàn cục được khai báo bên ngoài hàm, với giá trị lần lượt là 10 và 20.def tinh_tong(a, b):
: Hàmtinh_tong
nhận vào hai tham sốa
vàb
(tên gọi khác nhau không gây ảnh hưởng, chỉ quan trọng thứ tự).ket_qua = a + b
: Trong hàm, biếnket_qua
được tạo ra và gán giá trị bằng tổng củaa
vàb
.ket_qua
là một biến cục bộ vì nó được định nghĩa bên trong hàmtinh_tong
.print ("ket_qua là biến cục bộ:", ket_qua)
: In ra kết quả (biến cục bộ). Câu lệnh này chạy mà không gặp lỗi vìket_qua
đang trong phạm vi của hàmtinh_tong
.tinh_tong(x,y)
: Gọi hàm vớix
vày
làm đối sốprint(ket_qua)
: Dòng này gây ra lỗi. Python báo lỗiNameError: name 'ket_qua' is not defined
vì ta đang cố gắng sử dụng biếnket_qua
ở phạm vi toàn cục (bên ngoài hàm). Mà như chúng ta đã biết,ket_qua
chỉ có phạm vi hoạt động bên trong hàmtinh_tong
. Python không tìm thấy biếnket_qua
được định nghĩa ở phạm vi toàn cục, nên báo lỗi “Tên không được định nghĩa”.
Lời kết
Bài viết đã trình bày đầy đủ về variable scope trong Python. Giờ là lúc bạn áp dụng những kiến thức này vào dự án thực tế của mình. Hiểu rõ phạm vi biến không chỉ giúp code của bạn chạy trơn tru mà còn giúp bạn debug nhanh chóng. Chúc bạn thành công và luôn tìm thấy niềm vui trong lập trình!