Không có câu trả lời thấu đáo về thời gian Python3, vì vậy tôi đã trả lời ở đây. Hầu hết những gì được mô tả ở đây được trình bày chi tiết trong Nghị quyết 4.2.2 về tên của tài liệu Python 3.
Như được cung cấp trong các câu trả lời khác, có 4 phạm vi cơ bản, LEGB, dành cho Địa phương, Bao vây, Toàn cầu và Nội dung. Ngoài những thứ đó, còn có một phạm vi đặc biệt, thân lớp , không bao gồm một phạm vi kèm theo cho các phương thức được định nghĩa trong lớp; bất kỳ bài tập nào trong thân lớp làm cho biến từ đó bị ràng buộc trong thân lớp.
Đặc biệt, không có câu lệnh chặn, bên cạnh def
và class
, tạo ra một phạm vi biến. Trong Python 2, việc hiểu danh sách không tạo ra một phạm vi biến, tuy nhiên trong Python 3, biến vòng lặp trong phạm vi hiểu danh sách được tạo trong một phạm vi mới.
Để chứng minh tính đặc thù của cơ thể giai cấp
x = 0
class X(object):
y = x
x = x + 1 # x is now a variable
z = x
def method(self):
print(self.x) # -> 1
print(x) # -> 0, the global x
print(y) # -> NameError: global name 'y' is not defined
inst = X()
print(inst.x, inst.y, inst.z, x) # -> (1, 0, 1, 0)
Do đó, không giống như trong thân hàm, bạn có thể gán lại biến cho cùng tên trong thân lớp, để lấy biến lớp có cùng tên; Thay vào đó, tra cứu thêm về tên này giải quyết biến lớp.
Một trong những điều ngạc nhiên lớn hơn đối với nhiều người mới sử dụng Python là một for
vòng lặp không tạo ra một phạm vi biến. Trong Python 2, việc hiểu danh sách cũng không tạo ra một phạm vi (trong khi các trình tạo và hiểu chính xác thì có!) Thay vào đó chúng làm rò rỉ giá trị trong hàm hoặc phạm vi toàn cục:
>>> [ i for i in range(5) ]
>>> i
4
Việc hiểu có thể được sử dụng như một cách khôn ngoan (hoặc khủng khiếp nếu bạn muốn) để tạo các biến có thể sửa đổi trong các biểu thức lambda trong Python 2 - một biểu thức lambda không tạo ra một phạm vi biến, như trong def
câu lệnh, nhưng trong lambda không cho phép câu lệnh nào. Chuyển nhượng là một câu lệnh trong Python có nghĩa là không cho phép các phép gán biến trong lambda, nhưng việc hiểu danh sách là một biểu thức ...
Hành vi này đã được sửa trong Python 3 - không có biểu thức hiểu hoặc biến rò rỉ.
Toàn cầu thực sự có nghĩa là phạm vi mô-đun; mô-đun python chính là __main__
; tất cả các mô-đun nhập khẩu có thể truy cập thông qua sys.modules
biến; để có quyền truy cập vào __main__
một người có thể sử dụng sys.modules['__main__']
, hoặc import __main__
; việc truy cập và gán các thuộc tính ở đó là hoàn toàn chấp nhận được; chúng sẽ hiển thị dưới dạng các biến trong phạm vi toàn cầu của mô-đun chính.
Nếu một tên được gán cho trong phạm vi hiện tại (ngoại trừ trong phạm vi lớp), nó sẽ được coi là thuộc về phạm vi đó, nếu không, nó sẽ được coi là thuộc về bất kỳ phạm vi kèm theo nào được gán cho biến (có thể không được gán chưa, hoặc hoàn toàn không), hoặc cuối cùng là phạm vi toàn cầu. Nếu biến được coi là cục bộ, nhưng nó chưa được đặt hoặc đã bị xóa, việc đọc giá trị biến sẽ dẫn đến UnboundLocalError
, đó là một lớp con của NameError
.
x = 5
def foobar():
print(x) # causes UnboundLocalError!
x += 1 # because assignment here makes x a local variable within the function
# call the function
foobar()
Phạm vi có thể tuyên bố rằng nó rõ ràng muốn sửa đổi biến toàn cục (phạm vi mô-đun), với từ khóa toàn cầu:
x = 5
def foobar():
global x
print(x)
x += 1
foobar() # -> 5
print(x) # -> 6
Điều này cũng có thể ngay cả khi nó bị che khuất trong phạm vi kèm theo:
x = 5
y = 13
def make_closure():
x = 42
y = 911
def func():
global x # sees the global value
print(x, y)
x += 1
return func
func = make_closure()
func() # -> 5 911
print(x, y) # -> 6 13
Trong python 2 không có cách dễ dàng để sửa đổi giá trị trong phạm vi kèm theo; thông thường, điều này được mô phỏng bằng cách có một giá trị có thể thay đổi, chẳng hạn như danh sách có độ dài 1:
def make_closure():
value = [0]
def get_next_value():
value[0] += 1
return value[0]
return get_next_value
get_next = make_closure()
print(get_next()) # -> 1
print(get_next()) # -> 2
Tuy nhiên, trong python 3, nonlocal
giải cứu:
def make_closure():
value = 0
def get_next_value():
nonlocal value
value += 1
return value
return get_next_value
get_next = make_closure() # identical behavior to the previous example.
Các nonlocal
tài liệu nói rằng
Các tên được liệt kê trong một tuyên bố không nhắm mục tiêu, không giống như các tên được liệt kê trong một tuyên bố toàn cầu, phải đề cập đến các ràng buộc tồn tại trước trong một phạm vi kèm theo (phạm vi mà một ràng buộc mới sẽ được tạo ra không thể được xác định rõ ràng).
tức là nonlocal
luôn đề cập đến phạm vi không toàn cầu bên ngoài trong cùng, nơi tên đã được ràng buộc (nghĩa là được gán cho, bao gồm cả được sử dụng làm for
biến mục tiêu, trong with
mệnh đề hoặc như một tham số hàm).
Bất kỳ biến nào không được coi là cục bộ trong phạm vi hiện tại hoặc bất kỳ phạm vi kèm theo nào đều là biến toàn cục. Một tên toàn cầu được tra cứu trong từ điển toàn cầu mô-đun; nếu không tìm thấy, toàn cầu sẽ được tra cứu từ mô đun dựng sẵn; tên của mô-đun đã được thay đổi từ python 2 thành python 3; trong python 2 nó đã được __builtin__
và trong python 3 bây giờ nó được gọi builtins
. Nếu bạn gán cho một thuộc tính của mô đun dựng sẵn, nó sẽ hiển thị sau đó cho bất kỳ mô đun nào dưới dạng biến toàn cục có thể đọc được, trừ khi mô đun đó phủ bóng chúng bằng biến toàn cục của chính nó có cùng tên.
Đọc mô-đun dựng sẵn cũng có thể hữu ích; giả sử rằng bạn muốn chức năng in kiểu python 3 trong một số phần của tệp, nhưng các phần khác của tệp vẫn sử dụng print
câu lệnh. Trong Python 2.6-2.7, bạn có thể giữ print
chức năng Python 3 với:
import __builtin__
print3 = __builtin__.__dict__['print']
Thực from __future__ import print_function
tế không nhập print
hàm bất cứ nơi nào trong Python 2 - thay vào đó, nó chỉ vô hiệu hóa các quy tắc phân tích cú pháp cho print
câu lệnh trong mô-đun hiện tại, xử lý print
như bất kỳ định danh biến nào khác và do đó cho phép print
hàm được tra cứu trong các nội trang.