PHP
Python

Trang chủ

Variable Scope trong Python

Ngôn ngữ Python là một ngôn ngữ lập trình đa mục đích, nổi tiếng với cú pháp dễ đọc, dễ học và tính ứng dụng cao. Trong lĩnh vực phát triển web, Python thường được sử dụng thông qua các framework như Django và Flask để xây dựng các ứng dụng web mạnh mẽ, bảo mật và dễ mở rộng. Trong chuyên mục này, Vietnix không chỉ cung cấp kiến thức nền tảng về ngôn ngữ Python mà còn hướng dẫn chi tiết cách xây dựng các ứng dụng web thực tế, sử dụng các framework phổ biến và áp dụng các kỹ thuật tiên tiến. Vietnix cam kết liên tục cập nhật những bài viết mới nhất về các tính năng mới của Python, các thư viện hỗ trợ hữu ích và những phương pháp tốt nhất, giúp bạn khai thác tối đa sức mạnh của Python và hoàn thiện kỹ năng lập trình web của mình.
html
CSS
javascript
sql
python
php
c
c++
bootstrap
react
mysql
reactjs
vuejs
Javascript Tutorials
10/01/2025
25 phút đọc
Theo dõi Vietnix trên

Variable Scope trong Python

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.

Phạm vi biến (variable scope) trong Python là khu vực mà một biến có thể sử dụng
Phạm vi biến (variable scope) trong Python là khu vực mà một biến có thể sử dụng

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_daichieu_rong (chiều dài và chiều rộng của hình chữ nhật).
  • dien_tich = chieu_dai * chieu_rong: Biến dien_tich được định nghĩa và tính toán giá trị dựa trên chieu_daichieu_rong. Đây là một ví dụ về biến cục bộ, có phạm vi trong hàm tinh_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ến dien_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àm tinh_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.1company_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àm tinh_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ục vat_rate để tính giá sản phẩm sau khi đã cộng thuế VAT. Biến gia_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ục company_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àm tinh_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ục vat_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ến thue_suatthu_nhap được khởi tạo trong hàm tinh_tien_thue_va_thu_nhap.
  • def tinh_thue():: Hàm này được định nghĩa bên trong hàm tinh_tien_thue_va_thu_nhap, nó là một hàm lồng nhau.
  • nonlocal thu_nhap, nonlocal thue_suat: Câu lệnh nonlocal khai báo rằng thu_nhapthue_suat là biến nonlocal, tức chúng không phải là biến cục bộ của tinh_thue mà là biến được định nghĩa ở phạm vi ngoài (trong tinh_tien_thue_va_thu_nhap). Điều này cho phép hàm con thay đổi các biến thu_nhapthue_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àm tinh_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:

Không gian tên này được sắp xếp lồng ghép vào nhau
Không gian tên này được sắp xếp lồng ghép vào nhau

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__’: , ‘__file__’: ‘C:\\Users\\YourUser\\examples\\your_code.py’, ‘__cached__’: None, ‘website’: ‘TechBlog’, ‘nam_phat_hanh’: 2023, ‘da_hoat_dong’: True, ‘tinh_tong’: }
Biến cục bộ bên trong hàm: 11 22
33

Giải thích kết quả trên:

  • globals(): Hàm globals() 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àm tinh_tong, cùng với vị trí của hàm trong bộ nhớ được liệt kê trong kết quả từ hàm globals(). 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àm tinh_tong, không được xuất hiện ở kết quả của globals(). 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àm tinh_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ên tinh_tong và hai tham số là x, y.
  • a = x + 10, b = y + 20: Các biến a, 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ến a, 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àm tinh_tong với x1y2, 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__’: , ‘__file__’: ‘C:\\…\\main.py’, ‘__cached__’: None, ‘ten_bai_giang’: ‘Python cơ bản’, ‘diem_so’: 70, ‘ket_qua’: True, ‘tinh_tong_va_hien_thi’: }
locals(): {‘x’: 5, ‘y’: 15, ‘tong’: 20}

Vì cả globals()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àm tinh_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àm tinh_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ằng get().

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àm in_diem_so, một biến local có cùng tên diem_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ến diem_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àm in_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àm in_diem_so kết thúc, dòng lệnh này in ra giá trị của biến diem_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ến diem_so.
  • diem_so = diem_so + 20: Đây là dòng lệnh gây ra lỗi. Bên trong hàm cap_nhat_diem, khi Python gặp lệnh gán này, Python sẽ coi diem_so là một biến cục bộ (vì ta đang cố gán một giá trị mới cho diem_so). Tuy nhiên, vì đây là lần đầu tiên Python thấy diem_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ỗi UnboundLocalError. 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ến diem_so, biến cục bộ của hàm sau khi ta đã cố gắng thao tác. (Lưu ý, vì lỗi UnboundLocalError đã 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ục diem_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 = 50var2 = 60: Hai dòng này khai báo hai biến toàn cục có tên là var1var2, 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ục var1var2.
  • 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ên var1 như là một phần tử trong dictionary (thông qua key của dictionary), sau đó tăng giá trị biến var1 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.
  • var2 = var2 + 20: Sau khi đã khai báo var2 là một biến toàn cục với global 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ủa var2 lên 20.
  • thay_doi_bien_toan_cuc(): Câu lệnh này gọi hàm thay_doi_bien_toan_cuc, dẫn đến việc hai biến var1var2 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ủa var1var2. 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àm tinh_tong nhận vào hai tham số ab (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ến ket_qua được tạo ra và gán giá trị bằng tổng của ab. ket_qua là một biến cục bộ vì nó được định nghĩa bên trong hàm tinh_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àm tinh_tong.
  • tinh_tong(x,y): Gọi hàm với xy làm đối số
  • print(ket_qua): Dòng này gây ra lỗi. Python báo lỗi NameError: name 'ket_qua' is not defined vì ta đang cố gắng sử dụng biến ket_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àm tinh_tong. Python không tìm thấy biến ket_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!

Phúc Trần

tại
Vietnix

Kết nối với mình qua

Icon Quote
Icon Quote

Học lập trình online cùng vietnix

Học lập trình online cùng Vietnix

PHPXem thêmThu gọn