亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python3中contextvars模塊是什么

發布時間:2020-08-03 10:24:54 來源:億速云 閱讀:164 作者:清晨 欄目:編程語言

不懂Python3中contextvars模塊是什么?其實想解決這個問題也不難,下面讓小編帶著大家一起學習怎么去解決,希望大家閱讀完這篇文章后大所收獲。

什么是上下文(Context)?

Context Variables,也就是「上下文變量」。

Python3中contextvars模塊是什么

Context是一個包含了相關環境內容的對象。這不是什么很高深的設計,其實和我們的日常生活也是息息相關的。

舉個比較實時的例子,權力的游戲第八季剛開播,如果你沒看過前七季,不了解過去的劇情、人物關系、過去的種種主線副線發展,去看第八季第一集是完全看不懂的,因為你缺失了這個美劇的上下文。

上下文就帶著這些信息,如果有一人非常了解過去的那些劇情甚至看過原著,Ta可以把那些第八季能關聯到的故事、劇情搞一個視頻剪輯(上下文對象),那么你不需要把過去完整的七季完整看一遍,可能花一個小時看看這個視頻(獲得上下文對象),就能繼續看第八季(完成之后的操作)。

Flask的設計中就包含了Context(下面不再說上下文,而統一用Context)。這個設計有什么用呢?簡單地說:可以在一些場景下隱式地傳遞變量

我們看一下Django和Sanic怎么傳遞請求對象Request:

# Django
from django.http import HttpResponse
def index(request):
    text = request.GET.get('text')
    return HttpResponse(f'Text is {text}')
# Sanic
from sanic import response
app = Sanic()
@app.route('/')
async def index(request):
    text = request.args.get('text')
    return response.text(f'Text is {text}')

這2個框架都有一個問題:視圖函數上要顯式的傳遞request(請求對象)。我們再看看Flask的效果:

from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def index():
    text = request.args.get('text')
    return f'Text is {text}'

在Flask中,request是import進來使用的(不需要就不用import),和視圖解耦了。這種設計下,不需要像Django/Sanic那樣把參數傳來傳去。

ThreadLocal

Flask怎么實現的呢?這就引出了ThreadLocal(本地線程)對象,看名字可以知道它是線程安全的,是單個線程自己的局部變量。Flask的實現中并沒有直接用Python的ThreadLocal,而是自己實現了一個Local類,除了支持線程還支持了Greenlet的協程。

Q: 那為什么不用全局變量呢? A: 由于存在GIL,全局變量的修改必須加鎖,會影響效率

先看一下線程庫中ThreadLocal的例子:

? cat threadlocal_example.py
import random
import threading
local_data = threading.local()
def show():
    name = threading.current_thread().getName()
    try:
        val = local_data.value
    except AttributeError:
        print(f'Thread {name}: No value yet')
    else:
        print(f'Thread {name}: {val}')
def worker():
    show()
    local_data.value = random.randint(1, 100)
    show()
for i in range(2):
    t = threading.Thread(target=worker)
    t.start()
? python threadlocal_example.py
Thread Thread-1: No value yet
Thread Thread-1: 78
Thread Thread-2: No value yet
Thread Thread-2: 64

可以感受到2個線程的狀態互不影響。回到Flask,請求Context在內部作為一個棧來維護(應用Context在另外一個棧)。每個訪問Flask的請求,會綁定到當前的Context,等請求結束后再銷毀。維護的過程由框架實現,開發者不需要關心,你只需要用flask.request就可以了,這樣就提高了接口的可讀性和擴展性。

contextvars例子

threading.local的隔離效果很好,但是他是針對線程的,隔離線程之間的數據狀態。但是現在有了asyncio,怎么辦?

biu~ 我們回到contextvars,這個模塊提供了一組接口,可用于管理、儲存、訪問局部Context的狀態。我們看個例子:

? cat contextvar_example.py
import asyncio
import contextvars
# 申明Context變量
request_id = contextvars.ContextVar('Id of request')
async def get():
    # Get Value
    print(f'Request ID (Inner): {request_id.get()}')
async def new_coro(req_id):
    # Set Value
    request_id.set(req_id)
    await get()
    print(f'Request ID (Outer): {request_id.get()}')
async def main():
    tasks = []
    for req_id in range(1, 5):
        tasks.append(asyncio.create_task(new_coro(req_id)))
    await asyncio.gather(*tasks)
asyncio.run(main())
? python contextvar_example.py
Request ID (Inner): 1
Request ID (Outer): 1
Request ID (Inner): 2
Request ID (Outer): 2
Request ID (Inner): 3
Request ID (Outer): 3
Request ID (Inner): 4
Request ID (Outer): 4

可以看到在數據狀態協程之間互不影響。注意上面contextvars.ContextVar的傳入的第一個參數(name)值是一個字符串,它主要是用來標識和調試的,并不一定要用一個單詞或者用下劃線連起來。

注意,這個模塊不僅僅給aio加入Context的支持,也用來替代threading.local()。

在Python 3.6使用contextvars

contextvars實現了PEP 567, 如果在Python3.6想使用可以用MagicStack/contextvars這個向后移植庫,它和標準庫都是同一個作者寫的,可以放心使用。用之前你需要安裝它:

pip install contextvars
aiotask_context

在Sanic里面request確實沒有用Context,那在aio體系里面怎么用呢?原來我會使用一個獨立的庫aiotask_context,在我的技術博客項目中就有用到,我簡化一下這部分的代碼(延伸閱讀3的commit):

# ext.py
import aiotask_context as context  # noqa
# app.py
from ext import context
client = None
@app.listener('before_server_start')
async def setup_db(app, loop):
    global client
    client = aiomcache.Client(config.MEMCACHED_HOST, config.MEMCACHED_PORT, loop=loop)
    loop.set_task_factory(context.task_factory)
@app.middleware('request')
async def setup_context(request):
    context.set('memcache', client)
# models/mc.py
_memcache = None
async def get_memcache():
    global _memcache
    if _memcache is not None:
        return _memcache
    memcache = context.get('memcache')
    _memcache = memcache
    return memcache

按執行過程,我解釋一下:

app.py默認client是None,在before_server_start中會設置初始化一個aiomcache.Client,用global設置給client

每次請求,通過context.set('memcache', client)把client設置到Context里面

在實際業務中,直接用context.get('memcache')獲取這個client。整個邏輯中見不到client傳來傳去,也不需要給request設置額外的屬性

有一點要提,在Python 3.6, context接受的參數必須是ContextVar對象,要這么寫:

if PY36:
    import contextvars
    memcache_var = contextvars.ContextVar('memcache')
else:
    memcache_var = 'memcache'
try:
    memcache = context.get(memcache_var)
except AttributeError:
    # Hack for debug mode
    memcache = None

這里捕獲了AttributeError,主要是在ipython中調試,由于沒有啟動Sanic所以沒有設置上下文,所以需要異常處理一下。

contextvars的真實例子

接著替換成contextvars(延伸閱讀鏈接4的commit):

# models/var.py
import contextvars
memcache_var = contextvars.ContextVar('memcache')
# app.py
from models.var import memcache_var
client = None
@app.listener('before_server_start')
async def setup_db(app, loop):
    global client
    client = aiomcache.Client(config.MEMCACHED_HOST, config.MEMCACHED_PORT, loop=loop)
@app.middleware('request')
async def setup_context(request):
    memcache_var.set(client)
# models/mc.py
from models.var import memcache_var
_memcache = None
async def get_memcache():
    global _memcache
    if _memcache is not None:
        return _memcache
    memcache = memcache_var.get()
    _memcache = memcache
    return memcache

在這種模式下,memcache(Redis)等實例對象不需要放在request對象里面,也不需要傳來傳去,而是放在一個上下文中,需要時直接通過memcache_var.get()就可以拿到,繼而操作緩存了。

感謝你能夠認真閱讀完這篇文章,希望小編分享Python3中contextvars模塊是什么內容對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,遇到問題就找億速云,詳細的解決方法等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

屏东市| 横山县| 伊宁县| 视频| 印江| 八宿县| 汉中市| 通海县| 麻阳| 罗定市| 河南省| 庆云县| 天台县| 湾仔区| 邛崃市| 徐州市| 丁青县| 平阳县| 乐昌市| 滦平县| 招远市| 成都市| 新巴尔虎左旗| 盘山县| 苍溪县| 潼关县| 方正县| 乾安县| 合肥市| 黄浦区| 林西县| 河间市| 汤原县| 文安县| 乐亭县| 石柱| 德庆县| 亳州市| 沙田区| 肥城市| 斗六市|