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 Context chứ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 Token có thể được sử dụng để khôi phục biến về giá trị trước đó thông qua phương thức ContextVar.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( 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( 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ức ContextVar.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ỏ đến Token.MISSING nế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 Context hiệ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 ContextVars tớ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àm copy_context().

Mỗi luồng có một chồng đối tượng Context hiệu quả riêng. current context là đối tượng Context ở đầu ngăn xếp của luồng hiện tại. Tất cả các đối tượng Context trong 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 ContextVar hoạ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ị ContextVar thông qua phương thức ContextVar.set() đều được ghi lại trong ngữ cảnh hiện tại. Phương thức ContextVar.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ề True nếu context có giá trị cho bộ var; trả lại False nế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, KeyError sẽ đượ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():
     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  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