当前位置: 首页 > news >正文

温州定制网站建设电话全栈网站开发工程师

温州定制网站建设电话,全栈网站开发工程师,免费部署网站,市辖区郑州网站建设简单来说#xff0c;上下文包括request_ctx(封装了request和session),app_request(封装了app和g)#xff0c;两个ctx都储存在一个叫做Local的数据结构中#xff0c;这个结构的作用就是会自动根据不同的线程id返回对应的数据#xff0c;然后通过一个叫做 LocalStark 的结构把… 简单来说上下文包括request_ctx(封装了request和session),app_request(封装了app和g)两个ctx都储存在一个叫做Local的数据结构中这个结构的作用就是会自动根据不同的线程id返回对应的数据然后通过一个叫做 LocalStark 的结构把 Local 封装成栈并提供pop和push 功能request_ctx,app_request的入栈就是通过它实现在程序中调用数据的时候通过一个叫做LocalProxy的结构加上偏函数partial来获取 上下文一直是计算机中难理解的概念在知乎的一个问题下面有个很通俗易懂的回答 每一段程序都有很多外部变量。只有像Add这种简单的函数才是没有外部变量的。一旦你的一段程序有了外部变量这段程序就不完整不能独立运行。你为了使他们运行就要给所有的外部变量一个一个写一些值进去。这些值的集合就叫上下文。– vzch 比如在 flask 中视图函数需要知道它执行情况的请求信息请求的 url参数方法等以及应用信息应用中初始化的数据库等才能够正确运行。最直观地做法是把这些信息封装成一个对象作为参数传递给视图函数。但是这样的话所有的视图函数都需要添加对应的参数即使该函数内部并没有使用到它。 flask 的做法是把这些信息作为类似全局变量的东西视图函数需要的时候可以使用 from flask import request 获取。但是这些对象和全局变量不同的是——它们必须是动态的因为在多线程或者多协程的情况下每个线程或者协程获取的都是自己独特的对象不会互相干扰。 那么如何实现这种效果呢如果对 python 多线程比较熟悉的话应该知道多线程中有个非常类似的概念 threading.local可以实现多线程访问某个变量的时候只看到自己的数据。内部的原理说起来也很简单这个对象有一个字典保存了线程 id 对应的数据读取该对象的时候它动态地查询当前线程 id 对应的数据。flask python 上下文的实现也类似后面会详细解释。 1、初步印象 flask 中有两种上下文application context 和 request context。上下文有关的内容定义在 globals.py 文件文件的内容也非常短 ################################### globals.py ################################### def _lookup_req_object(name):top _request_ctx_stack.topif top is None:raise RuntimeError(_request_ctx_err_msg)return getattr(top, name)def _lookup_app_object(name):top _app_ctx_stack.topif top is None:raise RuntimeError(_app_ctx_err_msg)return getattr(top, name)def _find_app():top _app_ctx_stack.topif top is None:raise RuntimeError(_app_ctx_err_msg)return top.app# context locals _request_ctx_stack LocalStack() _app_ctx_stack LocalStack() current_app LocalProxy(_find_app) request LocalProxy(partial(_lookup_req_object, request)) session LocalProxy(partial(_lookup_req_object, session)) g LocalProxy(partial(_lookup_app_object, g)) flask 提供两种上下文application context 和 request context 。app lication context 又演化出来两个量 current_app 和 g而 request context 则演化出来 request 和 session。 这里的实现用到了两个东西LocalStack 和 LocalProxy。它们两个的结果就是我们可以动态地获取两个上下文的内容在并发程序中每个视图函数都会看到属于自己的上下文而不会出现混乱。 2 存储上下文模拟local LocalStack 和 LocalProxy 都是 werkzeug 提供的。在分析这两个类之前我们先介绍这个文件另外一个基础的类 Local。Local 就是实现了类似 threading.local 的效果多线程或者多协程情况下全局变量的隔离效果。下面是它的代码 ################################## local.py ################################### # since each thread has its own greenlet we can just use those as identifiers # for the context. If greenlets are not available we fall back to the # current thread ident depending on where it is. try:from greenlet import getcurrent as get_ident except ImportError:try:from thread import get_identexcept ImportError:from _thread import get_identclass Local(object):__slots__ (__storage__, __ident_func__)def __init__(self):# 数据保存在 __storage__ 中后续访问都是对该属性的操作# 因为还没有实例化所以不能调用自己的__setattr__object.__setattr__(self, __storage__, {})object.__setattr__(self, __ident_func__, get_ident)def __call__(self, proxy):Create a proxy for a name.return LocalProxy(self, proxy)# 清空当前线程/协程保存的所有数据def __release_local__(self):self.__storage__.pop(self.__ident_func__(), None)# 下面三个方法实现了属性的访问、设置和删除。# 注意到内部都调用 self.__ident_func__ 获取当前线程或者协程的 id然后再访问对应的内部字典。# 如果访问或者删除的属性不存在会抛出 AttributeError。# 这样外部用户看到的就是它在访问实例的属性完全不知道字典或者多线程/协程切换的实现def __getattr__(self, name):try:return self.__storage__[self.__ident_func__()][name]except KeyError:raise AttributeError(name)def __setattr__(self, name, value):ident self.__ident_func__()storage self.__storage__try:storage[ident][name] valueexcept KeyError:storage[ident] {name: value}def __delattr__(self, name):try:del self.__storage__[self.__ident_func__()][name]except KeyError:raise AttributeError(name) 可以看到Local 对象内部的数据都是保存在 __storage__ 属性的这个属性变量是个嵌套的字典__storage__{ident:{key:value}}。最外面字典 key 是线程或者协程的 identityvalue 是另外一个字典这个内部字典就是用户自定义的 key-value 键值对。用户访问实例的属性就变成了访问内部的字典外面字典的 key 是自动关联的。__ident_func 是 协程的 get_current 或者线程的 get_ident从而获取当前代码所在线程或者协程的 id。 除了这些基本操作之外Local 还实现了 __release_local__ 用来清空析构当前线程或者协程的数据状态。__call__ 操作来创建一个 LocalProxy 对象LocalProxy 会在下面讲到。 3 操作 Local 将Local维护成栈 理解了 Local我们继续回来看另外两个类。 LocalStack 是基于 Local 实现的栈结构。如果说 Local 提供了多线程或者多协程隔离的属性访问那么 LocalStack 就提供了隔离的栈访问。下面是它的实现代码可以看到它提供了 push、pop 和 top 方法。 __release_local__ 可以用来清空当前线程或者协程的栈数据__call__ 方法返回当前线程或者协程栈顶元素的代理对象。 ################################## local.py ################################### class LocalStack(object):This class works similar to a :class:Local but keeps a stackof objects instead. def __init__(self):self._local Local()def __release_local__(self):self._local.__release_local__()def __call__(self):def_lookup():rv self.topif rv is None:raise RuntimeError(object unbound)return rvreturn LocalProxy(_lookup)# push、pop 和 top 三个方法实现了栈的操作# 可以看到栈的数据是保存在 self._local.stack 属性中的def push(self, obj):Pushes a new item to the stackrv getattr(self._local, stack, None)if rv is None:self._local.stack rv []rv.append(obj)return rvdef pop(self):Removes the topmost item from the stack, will return theold value or None if the stack was already empty.stack getattr(self._local, stack, None)if stack is None:return Noneelif len(stack) 1:release_local(self._local) # 调用的local.py下的函数,实际执行local类下的__release__local__return stack[-1]else:return stack.pop()# 返回栈顶元素propertydef top(self):The topmost item on the stack. If the stack is empty,None is returned.try:return self._local.stack[-1]except (AttributeError, IndexError):return None 我们在之前看到了 request context 的定义它就是一个 LocalStack 的实例 _request_ctx_stack LocalStack() _app_ctx_stack LocalStack() 它会当前线程或者协程的请求都保存在栈里等使用的时候再从里面读取。至于为什么要用到栈结构而不是直接使用 Local我们会在后面揭晓答案你可以先思考一下。 4、使用 代理LocalProxy 和 偏函数partial 获取栈中数据 LocalProxy 是一个 Local 对象的代理负责把所有对自己的操作转发给内部的 Local 对象。LocalProxy 的构造函数介绍一个 callable 的参数这个 callable 调用之后需要返回一个 Local 实例后续所有的属性操作都会转发给 callable 返回的对象。 implements_bool class LocalProxy(object):Acts as a proxy for a werkzeug local. Forwards all operations toa proxied object. The only operations not supported for forwardingare right handed operands and any kind of assignment.Example usage::from werkzeug.local import Locall Local()# these are proxiesrequest l(request)user l(user)from werkzeug.local import LocalStack_response_local LocalStack()# this is a proxyresponse _response_local()Whenever something is bound to l.user / l.request the proxy objectswill forward all operations. If no object is bound a :exc:RuntimeErrorwill be raised.To create proxies to :class:Local or :class:LocalStack objects,call the object as shown above. If you want to have a proxy to anobject looked up by a function, you can (as of Werkzeug 0.6.1) passa function to the :class:LocalProxy constructor::session LocalProxy(lambda: get_current_request().session).. versionchanged:: 0.6.1The class can be instantiated with a callable as well now.__slots__ (__local, __dict__, __name__, __wrapped__)def __init__(self, local, nameNone):object.__setattr__(self, _LocalProxy__local, local)object.__setattr__(self, __name__, name)if callable(local) and not hasattr(local, __release_local__):# local is a callable that is not an instance of Local or# LocalManager: mark it as a wrapped function.object.__setattr__(self, __wrapped__, local)def _get_current_object(self):Return the current object. This is useful if you want the realobject behind the proxy at a time for performance reasons or becauseyou want to pass the object into a different context.if not hasattr(self.__local, __release_local__):return self.__local()try:return getattr(self.__local, self.__name__)except AttributeError:raise RuntimeError(no object bound to %s % self.__name__)propertydef __dict__(self):try:return self._get_current_object().__dict__except RuntimeError:raise AttributeError(__dict__)def __repr__(self):try:obj self._get_current_object()except RuntimeError:return %s unbound % self.__class__.__name__return repr(obj)def __bool__(self):try:return bool(self._get_current_object())except RuntimeError:return Falsedef __unicode__(self):try:return unicode(self._get_current_object()) # noqaexcept RuntimeError:return repr(self)def __dir__(self):try:return dir(self._get_current_object())except RuntimeError:return []def __getattr__(self, name):if name __members__:return dir(self._get_current_object())return getattr(self._get_current_object(), name)def __setitem__(self, key, value):self._get_current_object()[key] valuedef __delitem__(self, key):del self._get_current_object()[key]if PY2:__getslice__ lambda x, i, j: x._get_current_object()[i:j]def __setslice__(self, i, j, seq):self._get_current_object()[i:j] seqdef __delslice__(self, i, j):del self._get_current_object()[i:j]__setattr__ lambda x, n, v: setattr(x._get_current_object(), n, v)__delattr__ lambda x, n: delattr(x._get_current_object(), n)__str__ lambda x: str(x._get_current_object())__lt__ lambda x, o: x._get_current_object() o__le__ lambda x, o: x._get_current_object() o__eq__ lambda x, o: x._get_current_object() o__ne__ lambda x, o: x._get_current_object() ! o__gt__ lambda x, o: x._get_current_object() o__ge__ lambda x, o: x._get_current_object() o__cmp__ lambda x, o: cmp(x._get_current_object(), o) # noqa__hash__ lambda x: hash(x._get_current_object())__call__ lambda x, *a, **kw: x._get_current_object()(*a, **kw)__len__ lambda x: len(x._get_current_object())__getitem__ lambda x, i: x._get_current_object()[i]__iter__ lambda x: iter(x._get_current_object())__contains__ lambda x, i: i in x._get_current_object()__add__ lambda x, o: x._get_current_object() o__sub__ lambda x, o: x._get_current_object() - o__mul__ lambda x, o: x._get_current_object() * o__floordiv__ lambda x, o: x._get_current_object() // o__mod__ lambda x, o: x._get_current_object() % o__divmod__ lambda x, o: x._get_current_object().__divmod__(o)__pow__ lambda x, o: x._get_current_object() ** o__lshift__ lambda x, o: x._get_current_object() o__rshift__ lambda x, o: x._get_current_object() o__and__ lambda x, o: x._get_current_object() o__xor__ lambda x, o: x._get_current_object() ^ o__or__ lambda x, o: x._get_current_object() | o__div__ lambda x, o: x._get_current_object().__div__(o)__truediv__ lambda x, o: x._get_current_object().__truediv__(o)__neg__ lambda x: -(x._get_current_object())__pos__ lambda x: (x._get_current_object())__abs__ lambda x: abs(x._get_current_object())__invert__ lambda x: ~(x._get_current_object())__complex__ lambda x: complex(x._get_current_object())__int__ lambda x: int(x._get_current_object())__long__ lambda x: long(x._get_current_object()) # noqa__float__ lambda x: float(x._get_current_object())__oct__ lambda x: oct(x._get_current_object())__hex__ lambda x: hex(x._get_current_object())__index__ lambda x: x._get_current_object().__index__()__coerce__ lambda x, o: x._get_current_object().__coerce__(x, o)__enter__ lambda x: x._get_current_object().__enter__()__exit__ lambda x, *a, **kw: x._get_current_object().__exit__(*a, **kw)__radd__ lambda x, o: o x._get_current_object()__rsub__ lambda x, o: o - x._get_current_object()__rmul__ lambda x, o: o * x._get_current_object()__rdiv__ lambda x, o: o / x._get_current_object()if PY2:__rtruediv__ lambda x, o: x._get_current_object().__rtruediv__(o)else:__rtruediv__ __rdiv____rfloordiv__ lambda x, o: o // x._get_current_object()__rmod__ lambda x, o: o % x._get_current_object()__rdivmod__ lambda x, o: x._get_current_object().__rdivmod__(o)__copy__ lambda x: copy.copy(x._get_current_object())__deepcopy__ lambda x, memo: copy.deepcopy(x._get_current_object(), memo) 这里实现的关键是把通过参数传递进来的 Local 实例保存在 __local 属性中并定义了 _get_current_object() 方法获取当前线程或者协程对应的对象。这里通过 “_LocalProxy__local” 设置的值后面可以通过 self.__local 来获取。 然后 LocalProxy 重写了所有的魔术方法名字前后有两个下划线的方法具体操作都是转发给代理对象的。继续回到 request context 的实现 _request_ctx_stack LocalStack() request LocalProxy(partial(_lookup_req_object, request)) session LocalProxy(partial(_lookup_req_object, session))再次看这段代码希望能看明白_request_ctx_stack 是多线程或者协程隔离的栈结构request 每次都会用 _lookup_req_object 栈头部的数据来获取保存在里面的 requst context。 上下文流程 那么请求上下文信息是什么被放在 stack 中呢还记得之前介绍的 wsgi_app() 方法有下面两行代码吗具体可参考Falsk session 源码解析 ctx self.request_context(environ) ctx.push()每次在调用 app.__call__ 的时候都会把对应的请求信息压栈最后执行完请求的处理之后把它出栈。先来看看request_context 这个 方法只有一行代码 def request_context(self, environ):return RequestContext(self, environ)它调用了 RequestContext并把 self 和请求信息的字典 environ 当做参数传递进去。追踪到 RequestContext 定义的地方它出现在 ctx.py 文件中代码如下 class RequestContext(object):The request context contains all request relevant information. It iscreated at the beginning of the request and pushed to the_request_ctx_stack and removed at the end of it. It will create theURL adapter and request object for the WSGI environment provided.def __init__(self, app, environ, requestNone):self.app app# 对environ进行第二次封装封装成一个Request对象if request is None:request app.request_class(environ) # request_class Request  实际执行为 request Request(environ)self.request requestself.url_adapter app.create_url_adapter(self.request)self.flashes None# 为session 赋值 Noneself.session Noneself._implicit_app_ctx_stack []self.preserved Falseself._preserved_exc Noneself._after_request_functions []self.match_request()def match_request(self):Can be overridden by a subclass to hook into the matchingof the request.try:url_rule, self.request.view_args \self.url_adapter.match(return_ruleTrue)self.request.url_rule url_ruleexcept HTTPException as e:self.request.routing_exception edef push(self):Binds the request context to the current context.# If an exception occurs in debug mode or if context preservation is# activated under exception situations exactly one context stays# on the stack. The rationale is that you want to access that# information under debug situations. However if someone forgets to# pop that context again we want to make sure that on the next push# its invalidated, otherwise we run at risk that something leaks# memory. This is usually only a problem in test suite since this# functionality is not active in production environments.top _request_ctx_stack.topif top is not None and top.preserved:top.pop(top._preserved_exc)# Before we push the request context we have to ensure that there# is an application context.app_ctx _app_ctx_stack.topif app_ctx is None or app_ctx.app ! self.app:app_ctx self.app.app_context()app_ctx.push()self._implicit_app_ctx_stack.append(app_ctx)else:self._implicit_app_ctx_stack.append(None)if hasattr(sys, exc_clear):sys.exc_clear()_request_ctx_stack.push(self)# Open the session at the moment that the request context is available.# This allows a custom open_session method to use the request context.# Only open a new session if this is the first time the request was# pushed, otherwise stream_with_context loses the session.if self.session is None:session_interface self.app.session_interfaceself.session session_interface.open_session(self.app, self.request)if self.session is None:self.session session_interface.make_null_session(self.app)if self.url_adapter is not None:self.match_request()def pop(self, exc_sentinel):Pops the request context and unbinds it by doing that. This willalso trigger the execution of functions registered by the:meth:~flask.Flask.teardown_request decorator... versionchanged:: 0.9Added the exc argument.app_ctx self._implicit_app_ctx_stack.pop()try:clear_request Falseif not self._implicit_app_ctx_stack:self.preserved Falseself._preserved_exc Noneif exc is _sentinel:exc sys.exc_info()[1]self.app.do_teardown_request(exc)# If this interpreter supports clearing the exception information# we do that now. This will only go into effect on Python 2.x,# on 3.x it disappears automatically at the end of the exception# stack.if hasattr(sys, exc_clear):sys.exc_clear()request_close getattr(self.request, close, None)if request_close is not None:request_close()clear_request Truefinally:rv _request_ctx_stack.pop()# get rid of circular dependencies at the end of the request# so that we dont require the GC to be active.if clear_request:rv.request.environ[werkzeug.request] None# Get rid of the app as well if necessary.if app_ctx is not None:app_ctx.pop(exc)assert rv is self, Popped wrong request context. (%r instead of %r) % (rv,self,)def auto_pop(self, exc):if self.request.environ.get(flask._preserve_context) or (exc is not None and self.app.preserve_context_on_exception):self.preserved Trueself._preserved_exc excelse:self.pop(exc)def __enter__(self):self.push()return selfdef __exit__(self, exc_type, exc_value, tb):# do not pop the request stack if we are in debug mode and an# exception happened. This will allow the debugger to still# access the request object in the interactive shell. Furthermore# the context can be force kept alive for the test client.# See flask.testing for how this works.self.auto_pop(exc_value)if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None:reraise(exc_type, exc_value, tb)def __repr__(self):return %s %s [%s] of %s % (self.__class__.__name__,self.request.url,self.request.method,self.app.name,)每个 request context 都保存了当前请求的信息比如 request 对象和 app 对象。在初始化的最后还调用了 match_request 实现了路由的匹配逻辑。 push 操作就是把该请求的 ApplicationContext如果 _app_ctx_stack 栈顶不是当前请求所在 app 需要创建新的 app context 和 RequestContext 有关的信息保存到对应的栈上压栈后还会保存 session 的信息 pop 则相反把 request context 和 application context 出栈做一些清理性的工作。 到这里上下文的实现就比较清晰了每次有请求过来的时候flask 会先创建当前线程或者进程需要处理的两个重要上下文对象把它们保存到隔离的栈里面这样视图函数进行处理的时候就能直接从栈上获取这些信息。 NOTE因为 app 实例只有一个因此多个 request 共享了 application context。 为什么使用两个上下文 为什么要把 request context 和 application context 分开每个请求不是都同时拥有这两个上下文信息吗为什么 request context 和 application context 都有实现成栈的结构每个请求难道会出现多个 request context 或者 application context 吗第一个答案是“灵活度”第二个答案是“多 application”。虽然在实际运行中每个请求对应一个 request context 和一个 application context但是在测试或者 python shell 中运行的时候用户可以单独创建 request context 或者 application context这种灵活度方便用户的不同的使用场景比如没有请求的时候创建数据库需要调用app以便了解数据的链接信息而且栈可以让 redirect 更容易实现一个处理函数可以从栈中获取重定向路径的多个请求信息。application 设计成栈也是类似测试的时候可以添加多个上下文另外一个原因是 flask 可以多个 application 同时运行: from werkzeug.wsgi import DispatcherMiddleware from frontend_app import application as frontend from backend_app import application as backendapplication DispatcherMiddleware(frontend, {/backend: backend })这个例子就是使用 werkzeug 的 DispatcherMiddleware 实现多个 app 的分发这种情况下 _app_ctx_stack 栈里会出现两个 application context。 为什么要用 LocalProxy 为什么要使用 LocalProxy不使用 LocalProxy 直接访问 LocalStack 的对象会有什么问题吗 首先明确一点Local 和 LocalStack 实现了不同线程/协程之间的数据隔离。在为什么用 LocalStack 而不是直接使用 Local 的时候我们说过这是因为 flask 希望在测试或者开发的时候允许多 app 、多 request 的情况。而 LocalProxy 也是因为这个才引入进来的 我们拿 current_app LocalProxy(_find_app) 来举例子。每次使用 current_app 的时候他都会调用 _find_app 函数然后对得到的变量进行操作。 如果直接使用 current_app _find_app() 有什么区别呢区别就在于我们导入进来之后current_app 就不会再变化了。如果有多 app 的情况就会出现错误比如 from flask import current_appapp create_app() admin_app create_admin_app()def do_something():with app.app_context():work_on(current_app)with admin_app.app_context():work_on(current_app)这里我们出现了嵌套的 app每个 with 上下文都需要操作其对应的 app如果不适用 LocalProxy 是做不到的。 对于 request 也是类似但是这种情况真的很少发生有必要费这么大的功夫增加这么多复杂度吗 其实还有一个更大的问题这个例子也可以看出来。比如我们知道 current_app 是动态的因为它背后对应的栈会 push 和 pop 元素进去。那刚开始的时候栈一定是空的只有在 with app.app_context() 这句的时候才把栈数据 push 进去。而如果不采用LocalProxy进行转发那么在最上面导入 from flask import current_app 的时候current_app 就是空的因为这个时候还没有把数据 push 进去后面调用的时候根本无法使用。 所以为什么需要 LocalProxy 呢简单总结一句话因为上下文保存的数据是保存在栈里的并且会动态发生变化。如果不是动态地去访问会造成数据访问异常。
http://www.pierceye.com/news/357335/

相关文章:

  • 珠海网站建设公司哪家好织梦网站怎么做下载地址
  • 西安商城网站建设公司宁夏住宅建设发展公司网站
  • 网站建设实现用户登录济南网站建设找老兵
  • 做网站还挣钱吗成都网站设计策划免费
  • 阿里云服务器怎么放网站百度开户渠道商哪里找
  • 网赢天下深圳网站建设建设一个小说网站
  • 北湖区网站建设哪家好什么做网站的公司好
  • 做司法亲子鉴定网站专业设计服务网站
  • 网站建设排序题wordpress菜单左对齐
  • 太原网站建设方案维护北京网站建设东轩seo
  • 网站弹屏广告怎么做的自适应网站建设特点
  • 有一个网站专门做民宿做数据可视化图的网站
  • 手机产品 网站建设网站建设费怎么记账
  • 网站页面设计好了后台如何添加构建平台还是搭建平台
  • 公司展示类网站模板中国校园网站做的比较好的学校
  • 锡山建设局网站白云做网站的公
  • 上海网站制作软件wordpress 访问控制
  • 西部数码助手网站后台管理优秀的包装设计案例
  • 建站教学做门户网站怎么赚钱
  • 怎么自己编程做网站成都住建平台
  • 林州二建集团建设有限公司网站免费做链接的app有哪些
  • 建设企业网站企业网上银行登录官网宁波网站建设公司名单推荐
  • 网站直播用php怎么做的书城网站开发的参考文献
  • 广州免费自助建站平台韩国出线了吗
  • asp.net网站开发实训爆款采集推广引流软件
  • 怎么把自己做的网站挂到外网上中文 网站模板
  • 篮球运动装备网站模板昆明网站seo多少钱
  • 建筑网站起名wordpress评论折叠
  • 东莞seo网站推广建设抖音开放平台注册
  • 怎么做淘宝客采集网站建设局考试通知文件网站