python中的魔法方法深入理解_第1页
python中的魔法方法深入理解_第2页
python中的魔法方法深入理解_第3页
python中的魔法方法深入理解_第4页
python中的魔法方法深入理解_第5页
免费预览已结束,剩余1页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、接触python也有一段时间了,python相关的框架和模块也接触了不少,希望把自己接触到的自己 觉得比较好的设计和实现分享给大家,于是取了一个charming python”的小标,算是给自己开了一个头吧,希望大家多多批评指正。:)from flask import requestflask是一个人气非常高的 python web框架,笔者也拿它写过一些大大小小的项目,flask有一个特性我非常的喜欢,就是无论在什么地方,如果你想要获取当前的request对象,只要简单的:代码如下:from flask import request#从当前request获取内容request.argsreq

2、uest.formsrequest.cookies非常简单好记,用起来也非常的友好。不过,简单的背后藏的实现可就稍微有一些复杂 了。跟随我的文章来看看其中的奥秘吧!两个疑问?在我们往下看之前,我们先提出两个疑问:疑问一 :request,看上去只像是一个静态的类实例,我们为什么可以直接使用request.args这样的表达式来获取当前request的args属性,而不用使用比如:代码如下:from flask import get_request#获取当前requestrequest = get_request()get_request().args这样的方式呢? flask是怎么把reque

3、st对应到当前的请求对象的呢?疑问二:在真正的生产环境中,同一个工作进程下面可能有很多个线程(又或者是协 程),就像我刚刚所说的,request这个类实例是怎么在这样的环境下正常工作的呢?要知道其中的秘密,我们只能从flask的源码开始看了。源码,源码,还是源码首先我们打开flask的源码,从最开始的 _init_.py来看看request是怎么出来的: 代码如下:# file: flask/_init_.pyfrom .globals import curre nt_app, g, request, sessi on, _request_ctx_stack# file: flask/glob

4、als.pyfrom fun ctools import partialfrom werkzeug .lo cal import localstack, localproxydef _lookup_req_object (n ame):&n bsp; &n bsp; top = _request_ctx_stack.top&n bsp; &n bsp; if top is none:&n bsp; &n bsp; &n bsp; &n bsp; raise run timeerror(work ing outside of request con text)&n bsp; &n bsp; re

5、tur n getattr(top, n ame)# con text locals_request_ctx_stack = localstack()request = localproxy(partial(_lookup_req_object, request)我们可以看到flask的request是从globals.py引入的,而这里的定义 request的代码为 request = localproxy(partial(_lookup_req_object, request), 女口果有不了解 partial 是什么东西 的同学需要先补下课,首先需要了解一下partial o不过我们可以

6、简单的理解为partial(func, request)就是使用request作为func的第一个默认参数来产生另外一个function。所以, partial(_lookup_req_object, request) 我们可以理解为:生成一个 callable 的 function ,这个 function 主要是从 _request_ctx_stack 这个 localstack 对象获取堆栈顶部的第一个requestco ntext对象,然后返回这个对象的request属性。这个werkzeug下的localproxy弓I起了我们的注意,让我们来看看它是什么吧: 代码如下:impleme

7、 nts_boolclass localproxy(object):&n bsp; &n bsp; acts as a proxy for a werkzeug local. &n bsp;forwards all operati ons to&n bsp; &n bsp; a proxied object. &n bsp;the only operati ons not supported for forward ing&n bsp; &n bsp; are right han ded opera nds and any kind of assig nment.&n bsp; &n bsp;

8、看前几句介绍就能知道它主要是做什么的了,顾名思义,localproxy主要是就一个proxy ,一个为werkzeug的local对象服务的代理。他把所以作用到自己的操作全部“转发”到它所代理的对象上去。那么,这个proxy通过python是怎么实现的呢?答案就在源码里:代码如下:#为了方便说明,我对代码进行了一些删减和改动impleme nts_boolclass localproxy(object):&n bsp; &n bsp; _slots_ = (_local, _dict_, _name_)&n bsp; &n bsp; def _init_(self, local, n ame=

9、non e):&n bsp; &n bsp; &n bsp; &n bsp; #这里有一个点需要注意一下,通过了_setattr_方法,self 的        #ocalproxy_local属性被设置成了 local,你可能会好奇        #这个属性名称为什么这么奇怪,其实这是因为python不支持真正的        # private member,具体可以参见官方文档:        # 在这里你只要把它当做self.

10、_local = local 就可以了 :)&n bsp; &n bsp; &n bsp; &n bsp; object._setattr_(self, _localproxy_local, local)&n bsp; &n bsp; &n bsp; &n bsp; object._setattr_(self, _name_, n ame)&n bsp; &n bsp; def _get_curre nt_object(self):&n bsp; &n bsp; &n bsp; &n bsp;获取当前被代理的真正对象,一般情况下不会主动调用这个方法,除非你因为&n bsp; &n bsp; &

11、n bsp; &n bsp;某些性能原因需要获取做这个被代理的真正对象,或者你需要把它用来另外的&n bsp; &n bsp; &n bsp; &n bsp;地方。        #这里主要是判断代理的对象是不是一个werkzeug的local对象,在我们分析 request&n bsp; &n bsp; &n bsp; &n bsp; # 的过程中,不会用到这块逻辑。从 localproxy(partial(_lookup_req_object, 通过调用self._local()方法,我们得到了 也就是、_request_ctx_stack.top.

12、request&n bsp; &n bsp; &n bsp; &n bsp; if not hasattr(self._local, _release_local_):&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; # request)看来&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; # partial(_lookup_req_object, request)。&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; #&n bsp; &n bsp; &n b

13、sp; &n bsp; &n bsp; &n bsp; retur n self._local()&n bsp; &n bsp; &n bsp; &n bsp; try:&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; retur n getattr(self._local, self._name_)&n bsp; &n bsp; &n bsp; &n bsp; except attributeerror:&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; raise run timeerror( no

14、object bound to %s % self._name_)    #接下来就是一大段一段的python的魔法方法了,local proxy重载了(几乎)?所有python    #内建魔法方法,让所有的关于他自己的operations都指向到了_get_curre nt_object()    #所返回的对象,也就是真正的被代理对象。&n bsp; &n bsp;&n bsp; &n bsp; _setattr_ = lambda x, n, v: setattr(x._get_curre nt_object(), n, v)

15、&n bsp; &n bsp; _delattr_ = lambda x, n: delattr(x._get_curre nt_object(), n)&n bsp; &n bsp; _str_ = lambda x: str(x._get_curre nt_object()&n bsp; &n bsp; _lt_ = lambda x, o: x._get_curre nt_object() < o&n bsp; &n bsp; _le_ = lambda x, o: x._get_curre nt_object() <= o&n bsp; &n bsp; _eq_ = lam

16、bda x, o: x._get_curre nt_object() = o&n bsp; &n bsp; _ne_ = lambda x, o: x._get_curre nt_object() != o&n bsp; &n bsp; _gt_ = lambda x, o: x._get_curre nt_object() > o&n bsp; &n bsp; _ge_ = lambda x, o: x._get_curre nt_object() >= o&n bsp; &n bsp;事情到了这里,我们在文章开头的第二个疑问就能够得到解答了,我们之所以不需要使用get_requ

17、est()这样的方法调用来获取当前的request对象,都是localproxy的功劳。localproxy作为一个代理,通过自定义魔法方法。代理了我们对于request的所有操作,使之指向到真正的request对象。怎么样,现在知道了request.args不是它看上去那么简简单单的吧。现在,让我们来看看第二个问题,在多线程的环境下,request是怎么正常工作的呢?还是让我们回到globals.py吧:代码如下:from fun ctools import partialfrom werkzeug .lo cal import localstack, localproxy def _loo

18、kup_req_object (n ame):&n bsp; &n bsp; top = _request_ctx_stack.top&n bsp; &n bsp; if top is none:&n bsp; &n bsp; &n bsp; &n bsp; raise run timeerror(work ing outside of request con text)&n bsp; &n bsp; return getattr(top, n ame)# con text locals_request_ctx_stack = localstack()request = localproxy(

19、partial(_lookup_req_object, request)问题的关键就在于这个_request_ctx_stack对象了,让我们找到localstack的源码:代码如下:class localstack(object):&n bsp; &n bsp; def _init_(self):&n bsp; &n bsp; &n bsp; &n bsp; #&n bsp; &n bsp; &n bsp; &n bsp; #&n bsp; &n bsp; &n bsp; &n bsp; # 相关方法,比如 push、pop之类&n bsp; &n bsp; &n bsp; &n bsp;

20、#其实localstack主要还是用到了另外一个 local类它的一些关键的方法也被代理到了这个 local类上 相对于local类来说,它多实现了一些和堆栈stack”所以,我们只要直接看locaI代码就可以&n bsp; &n bsp; &n bsp; &n bsp; self._local = local()&n bsp; &n bsp;&n bsp; &n bsp; property&n bsp; &n bsp; def top(self):&n bsp; &n bsp; &n bsp; &n bsp;返回堆栈顶部的对象&n bsp; &n bsp; &n bsp; &n bsp; t

21、ry:&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; retur n self._local.stack-1&n bsp; &n bsp; &n bsp; &n bsp; except (attributeerror, i ndexerror):&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; retur n none# 所以, 当我们调用_request_ctx_stack.top 时,其实是调用了request_ctx_stack._local.stack-1#让我们来看看local类是怎么实现的

22、吧,不过在这之前我们得先看一下下面出现的 get_ident 方法#首先尝试着从 greenlet导入getcurrent方法,这是因为如果 flask跑在了像gevent这种 容器下的时候#所以的请求都是以 greenlet作为最小单位,而不是thread线程。try:&n bsp; &n bsp; from gree nlet import getcurre nt as get_ide ntexcept importerror:&n bsp; &n bsp; try:&n bsp; &n bsp; &n bsp; &n bsp; from thread import get_ide nt&

23、n bsp; &n bsp; except importerror:&n bsp; &n bsp; &n bsp; &n bsp; from _thread import get_ide nt#总之,这个get_ident方法将会返回当前的协程 /线程id,这对于每一个请求都是唯一 的class local(object):&n bsp; &n bsp; _slots_ = (_storage_, _ident_func_)&n bsp; &n bsp; def _init_(self):&n bsp; &n bsp; &n bsp; &n bsp; object._setattr_(self

24、, _storage_, )&n bsp; &n bsp; &n bsp; &n bsp; object._setattr_(self, _ident_func_, get_ide nt)&n bsp; &n bsp;    #问题的关键就在于local类重载了 _getattr_和_setattr_这两个魔法方法&n bsp; &n bsp; def _getattr_(self, n ame):&n bsp; &n bsp; &n bsp; &n bsp; try:            # 在这里我们返回调用

25、了 self._ident_func_(),也就是当前的唯一id            #来作为 _storage_的 key&n bsp;&n bsp;&n bsp; &n bsp; &n bsp;&n bsp;returnself._storage_self._ident_func_() name&n bsp; &n bsp; &n bsp; &n bsp; except keyerror:&n bsp; &n bsp; &n bsp; &n bsp; &n bsp; &n bsp; raise attributeerr or(n ame)&n bsp; &n bsp; def _setattr_(self, n ame, value):&n bsp; &n bsp; &n bsp; &n bsp; ide nt = self._ident_func_()&n bsp; &n bsp; &n bsp; &

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论