Sửa lỗi 'UnboundLocalError: local variable referenced before assignment' trong Python

beginner🐍 Python2026-05-21| Python 2.x / 3.x — tất cả nền tảng (Linux, macOS, Windows)

Error Message

UnboundLocalError: local variable 'x' referenced before assignment
#python#phạm vi biến#unboundlocalerror#global

Lỗi Gặp Phải

UnboundLocalError: local variable 'x' referenced before assignment

Lỗi này cứ vài tháng lại làm tôi điêu một lần. Bạn có một biến rõ ràng là đang tồn tại, bạn đọc nó bên trong một hàm, và Python nổi giận. Phần khó hiểu là: biến đó thực sự tồn tại — chỉ là không tồn tại cục bộ. Python đã quyết định nó phải là biến cục bộ trước khi bạn kịp gán giá trị cho nó.

Tại Sao Python Báo Lỗi Này

Python xác định một biến là cục bộ hay toàn cục tại thời điểm biên dịch, không phải lúc chạy. Hễ phát hiện bất kỳ phép gán nào cho một tên biến ở bất kỳ đâu trong thân hàm, Python sẽ đánh dấu tên đó là cục bộ cho toàn bộ hàm — kể cả những dòng nằm trước phép gán đó.

Bẫy kinh điển:

count = 0

def increment():
    print(count)   # <-- UnboundLocalError ở đây
    count += 1     # Python thấy phép gán này → đánh dấu 'count' là biến cục bộ

increment()

count tồn tại ở phạm vi module. Nhưng không quan trọng. Dòng count += 1 là đủ để Python coi toàn bộ count trong hàm là biến cục bộ. Vì vậy, lệnh print(count) phía trên nó cố đọc một biến cục bộ chưa được gán giá trị. Và thế là lỗi.

Cách Sửa Từng Bước

Cách 1: Dùng global khi sửa đổi biến ở phạm vi module

Khi biến nằm ở phạm vi module và bạn cần thay đổi nó bên trong hàm, hãy khai báo tường minh:

count = 0

def increment():
    global count      # báo cho Python biết: 'count' tham chiếu đến biến ở phạm vi module
    print(count)      # đọc count ở phạm vi module → 0
    count += 1

increment()
print(count)  # 1

Một lưu ý: chỉ dùng global khi bạn thực sự cần thay đổi biến bên ngoài. Nếu bạn chỉ đọc nó — không có phép gán nào trong hàm — thì không cần dùng global.

Cách 2: Dùng nonlocal cho hàm lồng nhau (closure)

Cùng một vấn đề nhưng ở phạm vi khác. Khi biến thuộc về hàm bao ngoài chứ không phải phạm vi module:

def outer():
    count = 0

    def inner():
        nonlocal count   # tham chiếu đến 'count' của outer()
        count += 1
        print(count)

    inner()  # in ra 1
    inner()  # in ra 2

outer()

nonlocal được thêm vào từ Python 3. Trong Python 2, closure không thể gán lại tên biến ở phạm vi ngoài — cách giải quyết thông thường là bọc giá trị trong một container có thể thay đổi như list một phần tử: count = [0], rồi dùng count[0] += 1.

Cách 3: Khởi tạo biến cục bộ trước

Đôi khi cách sửa đơn giản hơn — bạn vô tình dùng lại một tên biến trùng với biến bên ngoài:

# Lỗi: cố đọc 'result' trước khi được gán
def compute(data):
    if data:
        result = process(data)
    return result   # UnboundLocalError nếu data là falsy

# Đã sửa: gán giá trị mặc định trước
def compute(data):
    result = None
    if data:
        result = process(data)
    return result

Cách 4: Tái cấu trúc để tránh việc thay đổi biến hoàn toàn

Thành thật mà nói, đây thường là cách sạch sẽ nhất. Truyền giá trị vào, trả về giá trị mới, bỏ hẳn global:

count = 0

def increment(n):
    return n + 1

count = increment(count)
print(count)  # 1

Hàm thuần túy dễ kiểm thử hơn. Chúng cũng làm cho toàn bộ loại lỗi này trở nên không thể xảy ra.

Các Trường Hợp Ít Rõ Ràng Hơn

Gán giá trị có điều kiện

x = 10

def maybe_assign(flag):
    if flag:
        x = 99      # Python đánh dấu 'x' là cục bộ cho toàn bộ hàm
    print(x)        # UnboundLocalError khi flag là False

maybe_assign(False)

Có hai cách: khởi tạo x ở đầu thân hàm, hoặc khai báo global x. Cả hai đều được; cách khởi tạo thường gọn hơn.

Phép gán kết hợp (+=, -=, v.v.)

count += 1 là cú pháp rút gọn của count = count + 1. Phép gán ở vế trái khiến Python phân loại count là biến cục bộ. Vế phải sau đó cố đọc biến cục bộ đó (chưa được gán). Đây là nguyên nhân phổ biến nhất gây ra lỗi này — đáng nhớ kỹ.

Bên trong khối try/except

def load():
    try:
        data = fetch()
    except Exception:
        pass
    return data   # UnboundLocalError nếu fetch() ném ngoại lệ

Khi fetch() ném ngoại lệ, phép gán không bao giờ xảy ra và data vẫn chưa được gán. Cách sửa: thêm data = None trước khối try.

Kiểm Tra Sau Khi Sửa

Chạy hàm trực tiếp với đầu vào đã gây ra lỗi ban đầu:

python your_script.py

# hoặc trong REPL:
>>> increment()
0
>>> print(count)
1

Không còn UnboundLocalError và giá trị trả về trông đúng? Vậy là xong.

Với trường hợp gán có điều kiện, hãy kiểm thử cả hai nhánh — flag=True và flag=False. Lỗi chỉ xuất hiện trên một nhánh, nên một lần kiểm thử thành công là chưa đủ.

Bảng Tóm Tắt Nhanh

  • Sửa đổi biến ở phạm vi module bên trong hàm → thêm global varname
  • Sửa đổi biến của hàm bao ngoài → thêm nonlocal varname
  • Biến chỉ được gán trong một nhánh if/try → khởi tạo trước nhánh đó
  • Chỉ đọc, không gán → không cần từ khóa gì; Python đã thấy phạm vi ngoài
  • Cách sửa tốt nhất về lâu dài → tránh dùng global có thể thay đổi; truyền giá trị qua tham số và trả về kết quả

Related Error Notes