contextvars --- Biến ngữ cảnh¶
Mô-đun này cung cấp các API để quản lý, lưu trữ và truy cập trạng thái cục bộ theo ngữ cảnh. Lớp ContextVar dùng để khai báo và làm việc với Context Variables. Nên sử dụng hàm copy_context() và lớp Context để quản lý bối cảnh hiện tại trong các khung không đồng bộ.
Trình quản lý bối cảnh có trạng thái nên sử dụng Biến bối cảnh thay vì threading.local() để ngăn trạng thái của chúng bất ngờ chuyển sang mã khác khi được sử dụng trong mã đồng thời.
Xem thêm PEP 567 để biết thêm chi tiết.
Added in version 3.7.
Biến bối cảnh¶
- class contextvars.ContextVar(name[, *, default])¶
Lớp này được sử dụng để khai báo Biến bối cảnh mới, ví dụ::
var: ContextVar[int] = ContextVar('var', default=42)
Tham số name bắt buộc được sử dụng cho mục đích xem xét nội tâm và gỡ lỗi.
Tham số default chỉ có từ khóa tùy chọn được trả về bởi
ContextVar.get()khi không tìm thấy giá trị nào cho biến trong ngữ cảnh hiện tại.Important: Các biến bối cảnh phải được tạo ở cấp mô-đun cao nhất và không bao giờ được đóng ở dạng đóng. Các đối tượng
Contextchứa các tham chiếu mạnh đến các biến ngữ cảnh, ngăn cản việc thu thập rác đúng cách.- name¶
Tên của biến. Đây là thuộc tính chỉ đọc.
Added in version 3.7.1.
- get([default])¶
Trả về giá trị cho biến bối cảnh cho bối cảnh hiện tại.
Nếu không có giá trị cho biến trong ngữ cảnh hiện tại, phương thức sẽ:
trả về giá trị của đối số default của phương thức, nếu được cung cấp; hoặc
trả về giá trị mặc định cho biến bối cảnh, nếu nó được tạo bằng một biến bối cảnh; hoặc
tăng
LookupError.
- set(value)¶
Gọi để đặt giá trị mới cho biến ngữ cảnh trong ngữ cảnh hiện tại.
Đối số value bắt buộc là giá trị mới cho biến ngữ cảnh.
Trả về một đối tượng
Tokencó thể được sử dụng để khôi phục biến về giá trị trước đó thông qua phương thứcContextVar.reset().Để thuận tiện, đối tượng mã thông báo có thể được sử dụng làm trình quản lý bối cảnh để tránh gọi
ContextVar.reset()theo cách thủ công:var = ContextVar('var', default='giá trị mặc định') với var.set('giá trị mới'): khẳng định var.get() == 'giá trị mới' khẳng định var.get() == 'giá trị mặc định'
Nó là viết tắt của:
var = ContextVar('var', default='giá trị mặc định') token = var.set('giá trị mới') thử: khẳng định var.get() == 'giá trị mới' cuối cùng: var.reset(mã thông báo) khẳng định var.get() == 'giá trị mặc định'
Added in version 3.14: Đã thêm hỗ trợ cho việc sử dụng mã thông báo làm trình quản lý bối cảnh.
- reset(token)¶
Đặt lại biến bối cảnh về giá trị mà nó có trước khi
ContextVar.set()tạo ra token được sử dụng.Ví dụ:
var = ContextVar('var') token = var.set('giá trị mới') # code sử dụng 'var'; var.get() trả về 'giá trị mới'. var.reset(mã thông báo) # After lệnh gọi thiết lập lại var không còn giá trị nữa, vì vậy # var.get() sẽ đưa ra LookupError.
Cùng một token không thể được sử dụng hai lần.
- class contextvars.Token¶
Các đối tượng Token được trả về bằng phương thức
ContextVar.set(). Chúng có thể được chuyển đến phương thứcContextVar.reset()để hoàn nguyên giá trị của biến về giá trị trước set tương ứng. Một mã thông báo không thể đặt lại biến ngữ cảnh nhiều lần.Mã thông báo hỗ trợ context manager protocol để tự động đặt lại các biến ngữ cảnh. Xem
ContextVar.set().Added in version 3.14: Đã thêm hỗ trợ cho việc sử dụng làm trình quản lý bối cảnh.
- var¶
Thuộc tính chỉ đọc. Trỏ tới đối tượng
ContextVarđã tạo mã thông báo.
- old_value¶
Thuộc tính chỉ đọc. Đặt thành giá trị mà biến có trước lệnh gọi phương thức
ContextVar.set()đã tạo mã thông báo. Nó trỏ đếnToken.MISSINGnếu biến không được đặt trước cuộc gọi.
- MISSING¶
Một đối tượng đánh dấu được sử dụng bởi
Token.old_value.
Quản lý bối cảnh thủ công¶
- contextvars.copy_context()¶
Trả về bản sao của đối tượng
Contexthiện tại.Đoạn mã sau lấy một bản sao của ngữ cảnh hiện tại và in tất cả các biến cũng như giá trị của chúng được đặt trong đó:
ctx: Bối cảnh = copy_context() in(danh sách(ctx.items()))
Hàm này có độ phức tạp O(1), tức là hoạt động nhanh như nhau đối với các ngữ cảnh có một vài biến ngữ cảnh và đối với các ngữ cảnh có nhiều biến ngữ cảnh.
- class contextvars.Context¶
Ánh xạ của
ContextVarstới các giá trị của chúng.Context()tạo một ngữ cảnh trống không có giá trị nào trong đó. Để có bản sao của ngữ cảnh hiện tại, hãy sử dụng hàmcopy_context().Mỗi luồng có một chồng đối tượng
Contexthiệu quả riêng. current context là đối tượngContextở đầu ngăn xếp của luồng hiện tại. Tất cả các đối tượngContexttrong ngăn xếp được coi là entered.Entering một bối cảnh, có thể được thực hiện bằng cách gọi phương thức
run()của nó, biến bối cảnh thành bối cảnh hiện tại bằng cách đẩy nó lên trên cùng của ngăn xếp bối cảnh của luồng hiện tại.Exiting từ ngữ cảnh hiện tại, có thể được thực hiện bằng cách quay lại từ lệnh gọi lại được truyền tới phương thức
run(), khôi phục ngữ cảnh hiện tại về trạng thái trước khi ngữ cảnh được nhập bằng cách bật ngữ cảnh ra khỏi đầu ngăn xếp ngữ cảnh.Vì mỗi luồng có ngăn xếp ngữ cảnh riêng nên các đối tượng
ContextVarhoạt động theo kiểu tương tự nhưthreading.local()khi các giá trị được gán trong các luồng khác nhau.Việc cố gắng nhập ngữ cảnh đã nhập, bao gồm cả ngữ cảnh đã nhập trong các luồng khác, sẽ gây ra
RuntimeError.Sau khi thoát khỏi ngữ cảnh, nó có thể được nhập lại sau đó (từ bất kỳ chuỗi nào).
Mọi thay đổi đối với giá trị
ContextVarthông qua phương thứcContextVar.set()đều được ghi lại trong ngữ cảnh hiện tại. Phương thứcContextVar.get()trả về giá trị được liên kết với ngữ cảnh hiện tại. Việc thoát khỏi ngữ cảnh sẽ hoàn nguyên một cách hiệu quả mọi thay đổi được thực hiện đối với các biến ngữ cảnh trong khi ngữ cảnh được nhập (nếu cần, các giá trị có thể được khôi phục bằng cách nhập lại ngữ cảnh).Bối cảnh thực hiện giao diện
collections.abc.Mapping.- run(callable, *args, **kwargs)¶
Vào Ngữ cảnh, thực thi
callable(*args, **kwargs), sau đó thoát khỏi Ngữ cảnh. Trả về giá trị trả về của callable hoặc truyền bá một ngoại lệ nếu xảy ra.Ví dụ:
nhập ngữ cảnh var = contextvars.ContextVar('var') var.set('thư rác') print(var.get()) # 'spam' ctx = bối cảnh.copy_context() chắc chắn chính(): # 'var' trước đây được đặt thành 'spam' # calling 'copy_context()' và 'ctx.run(main)', vì vậy: print(var.get()) # 'spam' print(ctx[var]) # 'spam' var.set('ham') # Now, sau khi đặt 'var' thành 'ham': print(var.get()) # 'ham' print(ctx[var]) # 'ham' # Any thay đổi hàm 'main' thành 'var' # will được chứa trong 'ctx'. ctx.run(chính) Hàm # The 'main()' đã được chạy trong ngữ cảnh 'ctx', # so thay đổi thành 'var' được chứa trong đó: print(ctx[var]) # 'ham' # However, ngoài 'ctx', 'var' vẫn được đặt thành 'spam': print(var.get()) # 'spam'
- copy()¶
Trả về một bản sao nông của đối tượng bối cảnh.
- var in context
Trả về
Truenếu context có giá trị cho bộ var; trả lạiFalsenếu không.
- context[var]
Trả về giá trị của biến var
ContextVar. Nếu biến không được đặt trong đối tượng ngữ cảnh,KeyErrorsẽ được nâng lên.
- get(var[, default])¶
Trả về giá trị cho var nếu var có giá trị trong đối tượng ngữ cảnh. Trả về default nếu không. Nếu default không được cung cấp, hãy trả về
None.
- iter(context)
Trả về một trình vòng lặp qua các biến được lưu trữ trong đối tượng ngữ cảnh.
- len(proxy)
Trả về số lượng biến được đặt trong đối tượng bối cảnh.
- keys()¶
Trả về danh sách tất cả các biến trong đối tượng ngữ cảnh.
- values()¶
Trả về danh sách giá trị của tất cả các biến trong đối tượng ngữ cảnh.
- items()¶
Trả về danh sách gồm 2 bộ chứa tất cả các biến và giá trị của chúng trong đối tượng ngữ cảnh.
hỗ trợ asyncio¶
Các biến bối cảnh được hỗ trợ nguyên bản trong asyncio và sẵn sàng sử dụng mà không cần bất kỳ cấu hình bổ sung nào. Ví dụ: đây là một máy chủ echo đơn giản, sử dụng biến ngữ cảnh để cung cấp địa chỉ của máy khách từ xa trong Tác vụ xử lý máy khách đó:
nhập asyncio
nhập ngữ cảnh
client_addr_var = contextvars.ContextVar('client_addr')
chắc chắn render_goodbye():
Có thể truy cập địa chỉ # The của máy khách hiện đang được xử lý
# without chuyển nó một cách rõ ràng tới hàm này.
client_addr = client_addr_var.get()
return f'Tạm biệt khách hàng @ {client_addr}\r\n'.encode()
async def hand_request(reader, writer):
addr = writer.transport.get_extra_info('socket').getpeername()
client_addr_var.set(addr)
# In bất kỳ mã nào chúng tôi gọi hiện đều có thể nhận được
địa chỉ của # client bằng cách gọi 'client_addr_var.get()'.
trong khi Đúng:
dòng = đang chờ reader.readline()
in (dòng)
nếu không phải là line.strip():
phá vỡ
writer.write(b'HTTP/1.1 200 OK\r\n') dòng # status
writer.write(b'\r\n') # headers
writer.write(render_goodbye()) # body
nhà văn.close()
async def main():
srv = đang chờ asyncio.start_server(
hand_request, '127.0.0.1', 8081)
không đồng bộ với srv:
đang chờ srv.serve_forever()
asyncio.run(chính())
# To kiểm tra xem bạn có thể sử dụng telnet hoặc cuộn tròn:
# telnet 127.0.0.1 8081
# curl 127.0.0.1:8081