Về biến trong Python

Em có 1 đoạn code Python như sau:

def do():
	def run():
		if (i > MAX):
			print("Done")
			return None
		else:
			print(i)
			i += 1
			from time import sleep
			sleep(0.1)
			run()

	MAX = 100
	i = 0
	run()

do()

Code này em bị lỗi UnboundLocalError: local variable 'i' referenced before assignment. Dòng i = 0 dù em có khai báo trước hay sau run() đều bị lỗi như trên. Khi em thêm dòng nonlocal i vào trước print(i) thì compile được và có dòng warning SyntaxWarning: name 'i' is used prior to nonlocal declaration.
Em muốn thắc mắc là, tại sao biến MAX không cần khai báo global hay nonlocal mà biến i phải khai báo nonlocal (và có thể là global)?

Cách python tìm kiếm biến để xử lý như sau:

Nếu biến chỉ được gọi (reference) thì python sẽ tìm tuần tự từ scope hiện tại đến scope lớn hơn cho đến khi tìm được biến.

Ngược lại nếu biến được gán (assign) thì khi này giá trị biến sẽ bị thay đổi (thực ra chính xác ở đây là ô nhớ mà biến trỏ tới bị thay đổi). Do đó để tránh việc biến ở những scope ngoài bị thay đổi không mong muốn, python sẽ chỉ tìm trong scope hiện tại và trong danh sách những biến được chỉ định global (nonLocal…).

Trong ví dụ của bạn, trong scope của run(), MAX chỉ được reference nhưng i có khi được assign (i =+ 1). Khi i được assign, trong scope run() chưa tồn tại biến i nên báo lỗi như trên.

Để tránh các lỗi không mong muốn trong python (cũng như các ngôn ngữ khác), tốt nhất nên giới hạn scope của biến càng nhỏ càng tốt bằng cách khai báo biến trong function hiện tại trước khi sử dụng.

Có thể cải thiện ví dụ trên như sau:

def do():
    def run(i, MAX):
        # code...
    
    run(0, 100)

do()
6 Likes

mình nghĩ bạn cần khi báo i ngày sau khi dùng lệnh def hoặc là khai báo trước khi dùng lệnh if (ì > max): ! bạn thử xem sao

Thứ nhất, i của mình muốn để ở trong do() chứ không phải ở trong run(), vì mình còn dùng i ở ngoài run() mà ở trong do() nữa; nên mình không thể khai báo i trong trước if được.

Thứ 2, bạn khuyên mình khai báo i ngay sau khi dùng def, vậy bạn đã thử chạy code chưa?

Hãy là người comment có trách nhiệm. Không phải cứ comment bừa là được đâu.

1 Like
def do():
	def run(i,MAX):
		if (i > MAX):
			print("Done")
			return None
		else:
			print(i)
			i += 1
			from time import sleep
			sleep(0.1)
			run(i,MAX)

	MAX = 100
	i = 0
	run(i,MAX)

do()

muốn gọi nó ra trong def thì phải truyền nó vào thôi. ko thể sai vào đâu được

Vì MAX không thay đổi còn i thay đổi trong hàm run()

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?