Python hơi kỳ lạ ở chỗ nó giữ mọi thứ trong từ điển cho các phạm vi khác nhau. Bản gốc a, b, c nằm trong phạm vi trên cùng và do đó trong từ điển cao nhất đó. Các chức năng có từ điển riêng của mình. Khi bạn tiếp cận print(a)
và print(b)
phát biểu, không có gì có tên đó trong từ điển, vì vậy Python tìm danh sách và tìm thấy chúng trong từ điển toàn cầu.
Bây giờ chúng ta nhận c+=1
được, tất nhiên, tương đương với c=c+1
. Khi Python quét dòng đó, nó báo "aha, có một biến có tên c, tôi sẽ đưa nó vào từ điển phạm vi cục bộ của mình." Sau đó, khi nó tìm kiếm một giá trị cho c cho c ở phía bên phải của bài tập, nó tìm thấy biến cục bộ có tên c , chưa có giá trị nào và do đó ném lỗi.
Câu lệnh global c
được đề cập ở trên chỉ đơn giản nói với trình phân tích cú pháp rằng nó sử dụng c
từ phạm vi toàn cầu và do đó không cần một cái mới.
Lý do nó nói rằng có một vấn đề trên dòng đó là vì nó đang tìm kiếm tên một cách hiệu quả trước khi nó cố gắng tạo mã, và vì vậy trong một số ý nghĩa không nghĩ rằng nó thực sự đang thực hiện dòng đó. Tôi cho rằng đó là một lỗi sử dụng, nhưng nói chung, đó là một cách thực hành tốt để chỉ học cách không quá coi trọng các thông điệp của nhà soạn nhạc .
Nếu cảm thấy thoải mái, có lẽ tôi đã dành một ngày để đào bới và thử nghiệm vấn đề tương tự này trước khi tôi tìm thấy thứ gì đó mà Guido đã viết về từ điển giải thích mọi thứ.
Cập nhật, xem bình luận:
Nó không quét mã hai lần, nhưng nó quét mã theo hai giai đoạn, lexing và phân tích cú pháp.
Xem xét cách phân tích của dòng mã này hoạt động. Nhà từ vựng đọc văn bản nguồn và chia nó thành các từ vựng, "thành phần nhỏ nhất" của ngữ pháp. Vì vậy, khi nó chạm dòng
c+=1
nó phá vỡ nó thành một cái gì đó như
SYMBOL(c) OPERATOR(+=) DIGIT(1)
Trình phân tích cú pháp cuối cùng muốn biến nó thành một cây phân tích cú pháp và thực thi nó, nhưng vì nó là một nhiệm vụ, nên trước đó, nó tìm tên c trong từ điển cục bộ, không nhìn thấy nó và chèn nó vào từ điển, đánh dấu nó như chưa được khởi tạo. Trong một ngôn ngữ được biên dịch đầy đủ, nó sẽ chỉ đi vào bảng biểu tượng và chờ phân tích cú pháp, nhưng vì nó không có sự sang trọng của lần thứ hai, nên lexer làm thêm một chút để cuộc sống dễ dàng hơn về sau. Chỉ sau đó, nó nhìn thấy HOẠT ĐỘNG, thấy rằng các quy tắc nói "nếu bạn có một toán tử + = phía bên trái phải được khởi tạo" và nói "Rất tiếc!"
Vấn đề ở đây là nó chưa thực sự bắt đầu phân tích cú pháp . Đây là tất cả các loại chuẩn bị xảy ra cho phân tích cú pháp thực tế, vì vậy bộ đếm dòng đã không chuyển sang dòng tiếp theo. Do đó, khi nó báo hiệu lỗi, nó vẫn nghĩ nó ở dòng trước.
Như tôi nói, bạn có thể cho rằng đó là một lỗi sử dụng, nhưng thực ra đây là một điều khá phổ biến. Một số trình biên dịch trung thực hơn về nó và nói "lỗi trên hoặc xung quanh dòng XXX", nhưng trình biên dịch này thì không.