ce修改器找不到数值,为什么有些游戏的数值是用一串很长的数组表示的?怎么翻译?

CE修改器使用教程详细_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
CE修改器使用教程详细
&&游戏修改用工具
你可能喜欢CE修改器为什么改完数据一会又变回去了_百度知道
CE修改器为什么改完数据一会又变回去了
您的回答被采纳后将获得:
系统奖励15(财富值+成长值)+难题奖励30(财富值+成长值)
我有更好的答案
.,点击 &替换& 按钮,然后”确定“,选中它?”点“是”,这时刚才弹出的窗口内应该会出现1或2个汇编代码。会弹出一个空白的窗口,然后让怪打你,弹出提示框“将使用ce调试器调试当前进程.继续把你想改的地址选中,右键点击“找出是什么改写了这个地址”
为您推荐:
其他类似问题
西餐的相关知识
换一换
回答问题,赢新手礼包
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。查看: 1778|回复: 1
各位大神,有些游戏怎么用CE搜索不到数值!求解!
阅读权限30
在线时间 小时
有的游戏,用CE搜索第一遍数值能搜索到,但是第二次就什么也没有了,不管我用什么方法例如改变数值类型再搜索,告诉我怎么办,谢谢!在要搜索的游戏里搜一个数值可以搜到,但搜索另一个数值怎么搜索查找结果都是〇!帮帮解一下!
阅读权限90
在线时间 小时
搜索方法不对?还是搜索数值不对……或说加密之类的
Powered by&p&&b&简单说说Anaconda和Jupyter有什么作用?&/b&&/p&&p&&a href=&http://link.zhihu.com/?target=https%3A//anaconda.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Anaconda&/a& 是一个包含数据科学常用包的 Python 发行版本。它基于 conda ——一个包和环境管理器——衍生而来。Anaconda 实际上是一个软件发行版,它附带了 conda、Python 和 150 多个科学包及其依赖项。conda是包和环境的管理器。Anaconda 的下载文件比较大(约 500 MB),因为它附带了 Python 中最常用的数据科学包。如果只需要某些包,或者需要节省带宽或存储空间,也可以使用 &b&Miniconda&/b& 这个较小的发行版(仅包含 conda 和 Python)。但你仍可以使用 conda 来安装任何可用的包,它只是自身没有附带这些包而已。&/p&&p&&a href=&http://link.zhihu.com/?target=http%3A//jupyter.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jupyter notebook&/a& 是一种 Web 文档。写过项目的都知道,我们在编译器写代码,然后又去打开word或者其他的文本编辑工具去写开发文档,而且调试也不是非常的方便,是不是感觉特麻烦。 Jupyte的出现就解决我们的各种麻烦,能够让我们把文本,图像和代码全部组合在一个文档中,而且,调试也特别的方便,大大的提高我们开发的效率 ——简直就是一个神奇&/p&&p&&b&1.Anaconda&/b&&/p&&p&&b&安装 Anaconda&/b&&/p&&p&Anaconda 可用于 Windows、Mac OS X 和 Linux。可以在 &a href=&http://link.zhihu.com/?target=https%3A//www.continuum.io/downloads& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&https://www.&/span&&span class=&visible&&continuum.io/downloads&/span&&span class=&invisible&&&/span&&/a& 上找到安装程序和安装说明。&/p&&p&如果计算机上已经安装了 Python(或者自带了),这不会有任何影响。实际上,脚本和程序使用的默认 Python 是 Anaconda 附带的 Python。&/p&&p&安装的版本可以根据自己的电脑配置安装,安装过程中遇到的大多数问题都是环境的配置问题,聪明的人都会百度的&/p&&p&完成安装后,会自动进入默认的 conda 环境,而且所有包均已安装完毕,如下面所示。可以在终端或命令提示符中键入Conda list,以查看你安装的内容。&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/260480& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic2.zhimg.com/80/v2-ddb82d25ddc62f66009ac7d_b.jpg& data-lens-id=&260480&&
&img class=&thumbnail& src=&https://pic2.zhimg.com/80/v2-ddb82d25ddc62f66009ac7d_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/260480&/span&
&p&在 Windows 上,会随 Anaconda 一起安装一批应用程序:&/p&&ul&&li&Anaconda Navigator,它是用于管理环境和包的 GUI&/li&&li&Anaconda Prompt 终端,它可让你使用命令行界面来管理环境和包&/li&&/ul&&p&安装之后为了避免报错,最好在默认环境下更新所有的包。打开 Anaconda Prompt (或者 Mac 下的终端),输入:conda upgrade
--all,首次安装的时候软件包版本一般都比较老,因此提前更新可以避免未来不必要的问题。&/p&&p&&b&管理包&/b&&/p&&p&安装了 Anaconda 之后,管理包是相当简单的。要安装包,就在终端中键入conda install 要安装的包的名字。例如,要安装 numpy,请键入 conda install numpy&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/356096& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic3.zhimg.com/80/v2-653ac4aeb1af_b.jpg& data-lens-id=&356096&&
&img class=&thumbnail& src=&https://pic3.zhimg.com/80/v2-653ac4aeb1af_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/356096&/span&
&p&我们还可以同时安装多个包,类似于这样:conda install numpy numpy scipy pandas的命令同时安装多个包,同时也可以安装我们指定版本的包conda install numpy=1.1.0,1.1.0就是版本号,只要版本号存在都会安装的&/p&&p&conda还会自动安装依赖项,比如说:scipy依赖于numpy,因为使用scipy需要numpy,所以我们安装scipy的时候,会自动安装上Numpy(如果numpy尚未安装)&/p&&p&&b&卸载包&/b&&/p&&p&我们不想要某个包了,可以使用conda remove 包名&/p&&p&&b&更新包&/b&&/p&&p&我们想要更新包,可以使用conda update 包名,如果我们想要更新所有的包:conda udate --all&/p&&p&&b&管理环境&/b&&/p&&p&为了包的冲突等,我们常常使用conda来创建虚拟环境,如果想要创建环境:conda create -n env_name(-n 就是name的意思,env_name就是环境的名字,可以任意指定),创建环境之后我们可以使用conda env list来查看我们的环境是否创建成功(env是environment的缩写)&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/783168& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic3.zhimg.com/80/v2-d709de8ea04cacfd961993ae_b.jpg& data-lens-id=&783168&&
&img class=&thumbnail& src=&https://pic3.zhimg.com/80/v2-d709de8ea04cacfd961993ae_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/783168&/span&
&p&进入刚才创建的环境(进入环境命令的前面会是我们的环境名字)和退出环境&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/262784& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic1.zhimg.com/80/v2-085cf2ef7fed250bdeaaf0ae_b.jpg& data-lens-id=&262784&&
&img class=&thumbnail& src=&https://pic1.zhimg.com/80/v2-085cf2ef7fed250bdeaaf0ae_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/262784&/span&
&p&进入环境后,你会在终端提示符中看到环境名称,它类似于 &code&(my_env) ~ $&/code&。环境中只安装了几个默认的包,以及你在创建它时安装的包。我们可以使用检查这一点。在环境中安装包的命令与前面一样:conda install package_name不过,这次你安装的特定包仅在你进入环境后才可用。要离开环境,请键入 source deactivate(在 OSX/Linux 上)。在 Windows 上,请使用deactivate。&/p&&p&&b&保存和加载环境&/b&&/p&&p&这是一个很重要的功能,能让其他人安装我们的代码中使用的所有包,这样就避免不同的人会使用不用的版本冲突问题。可以输入:conda env export & envirment.yaml
conda env export是导出当前环境,& envirment.yaml就是保存的文件,然后如果别人想要直接创建跟你一样的环境开发项目就可以直接使用conda env create -f
envirment.yaml &/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/862080& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic4.zhimg.com/80/v2-15d8b29d4febe170a6b947_b.jpg& data-lens-id=&862080&&
&img class=&thumbnail& src=&https://pic4.zhimg.com/80/v2-15d8b29d4febe170a6b947_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/862080&/span&
&p&&b&删除环境&/b&&/p&&p&删除环境输入:conda env remove -n env_name(环境的名字)&/p&&p&更多conda命令:参考这篇 &a href=&http://link.zhihu.com/?target=http%3A//conda.pydata.org/docs/using/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&conda 文档&/a&。&/p&&p&&br&&/p&&p&&b&2.Jupyter&/b&&/p&&p&Jupyter notebook 是一种 Web 应用,能让用户将说明文本、数学方程、代码和可视化内容全部组合到一个易于共享的文档中&/p&&p&Jupyter notebook 源自 Fernando Perez 发起的 &a href=&http://link.zhihu.com/?target=https%3A//ipython.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&IPython 项目&/a&。IPython 是一种交互式 shell,与普通的 Python shell 相似,但具有一些很好的功能(例如语法高亮显示和代码补全)。最初,notebook 的工作方式是,将来自 Web 应用(你在浏览器中看到的 notebook)的消息发送给 IPython 内核(在后台运行的 IPython 应用程序)。内核执行代码,然后将结果发送回 notebook。当前架构与之相似,具体见下图。&/p&&figure&&img src=&http://pic1.zhimg.com/v2-bf60dc58f4033ebf639d4e_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&633& data-rawheight=&357& class=&origin_image zh-lightbox-thumb& width=&633& data-original=&http://pic1.zhimg.com/v2-bf60dc58f4033ebf639d4e_r.jpg&&&/figure&&p&核心是 notebook 的服务器。你通过浏览器连接到该服务器,而 notebook 呈现为 Web 应用。你在 Web 应用中编写的代码通过该服务器发送给内核,内核运行代码,并将结果发送回该服务器。之后,任何输出都会返回到浏览器中。保存 notebook 时,它将作为 JSON 文件(文件扩展名为 .ipynb)写入到该服务器中。&/p&&p&此架构的一个优点是,内核无需运行 Python。由于 notebook 和内核分开,因此可以在两者之间发送任何语言的代码。例如,早期的两个非 Python 内核分别是 &a href=&http://link.zhihu.com/?target=https%3A//www.r-project.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&R&/a& 语言和 &a href=&http://link.zhihu.com/?target=http%3A//julialang.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Julia&/a& 语言。使用 R 内核时,用 R 编写的代码将发送给执行该代码的 R 内核,这与在 Python 内核上运行 Python 代码完全一样。IPython notebook 已被改名,因为 notebook 变得与编程语言无关。新的名称 &b&Jupyter&/b& 由 &b&Ju&/b&lia、&b&Pyt&/b&hon 和 &b&R&/b& 组合而成。&/p&&p&另一个优点是,你可以在任何地方运行 notebook 服务器,并且可通过互联网访问服务器。通常,你会在存储所有数据和 notebook 文件的自有计算机上运行服务器&/p&&p&&br&&/p&&p&&b&安装 Jupyter notebook&/b&&/p&&p&安装 Jupyter 的最简单方法是使用 Anaconda.&/p&&p&要在 conda 环境中安装 Jupyter notebook,请使用 conda install jupyter notebook&/p&&p&也可以通过 pip 使用 pip insatll
jupyter notebook来获得 Jupyter notebook。&/p&&p&&br&&/p&&p&&b&Jupyter notebook 服务器 启动&/b&&/p&&p&启动notebook服务器,在终端输入jupyter notebook,运行完之后,浏览器会自动打开notebook.默认情况下,notebook 服务器的运行地址是 http://localhost:8888。该地址的含义是:localhost 表示你的计算机,而 8888 是服务器的通信端口。只要 notebook 服务器仍在运行,你随时都能通过在浏览器中输入http://localhost:8888 返回到 web 页面中。&/p&&p&如果同时启动了另一个 notebook 服务器,新服务器会尝试使用端口 &code&8888&/code&,但由于此端口已被占用,因此新服务器会在端口 8889 上运行。之后,你可以通过 http://localhost:8889 连接到新服务器。每个额外的 notebook 服务器都会像这样增大端口号。&/p&&p&&br&&/p&&p&如果启动notebook服务器,它应类似以下所示:&/p&&figure&&img src=&http://pic4.zhimg.com/v2-ebacb72af94b98d2570683_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1982& data-rawheight=&1270& class=&origin_image zh-lightbox-thumb& width=&1982& data-original=&http://pic4.zhimg.com/v2-ebacb72af94b98d2570683_r.jpg&&&/figure&&p&在右侧,你可以点击“New”(新建),创建新的 notebook、文本文件、文件夹或终端。“Notebooks”下的列表显示了你已安装的内核。由于我在 Python 2 环境中运行服务器,因此列出了 Python 2 内核。&/p&&p&如果在 conda 环境中运行 Jupyter notebook 服务器,则还能选择环境中任何其他的内核(见下图)。要创建新的 notebook,请点击你要使用的内核。&/p&&figure&&img src=&http://pic2.zhimg.com/v2-e96cbafa90e1_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&502& data-rawheight=&932& class=&origin_image zh-lightbox-thumb& width=&502& data-original=&http://pic2.zhimg.com/v2-e96cbafa90e1_r.jpg&&&/figure&&p&顶部的选项卡是 &i&Files&/i&(文件)、&i&Running&/i&(运行)和 &i&Cluster&/i&(集群)。&i&Files&/i&(文件)显示当前目录中的所有文件和文件夹。点击 &i&Running&/i&(运行)选项卡会列出所有正在运行的 notebook。可以在该选项卡中管理这些 notebook。&/p&&p&&b&关闭 Jupyter&/b&&/p&&p&通过在服务器主页上选中 notebook 旁边的复选框,然后点击“Shutdown”(关闭),你就可以关闭各个 notebook。但是,在这样做之前,请确保保存了工作!否则,在上次保存后所做的任何更改都会丢失。下次运行 notebook 时,还需要重新运行代码。&/p&&figure&&img src=&http://pic2.zhimg.com/v2-f7d5b6a4ca5_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1914& data-rawheight=&908& class=&origin_image zh-lightbox-thumb& width=&1914& data-original=&http://pic2.zhimg.com/v2-f7d5b6a4ca5_r.jpg&&&/figure&&p&还可以在终端中按两次 Ctrl + C,可以关闭整个服务器。再次提醒,这会立即关闭所有运行中的 notebook,所以确保保存了工作!&/p&&figure&&img src=&http://pic4.zhimg.com/v2-dca229fdea9fe95cf8271d12fad38227_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1128& data-rawheight=&726& class=&origin_image zh-lightbox-thumb& width=&1128& data-original=&http://pic4.zhimg.com/v2-dca229fdea9fe95cf8271d12fad38227_r.jpg&&&/figure&&p&&b&Notebook 界面&/b&&/p&&p&创建新的 notebook 时,你会看到如下所示的界面:&/p&&figure&&img src=&http://pic1.zhimg.com/v2-ccf835edbad7bef5ba9c0_b.jpg& data-caption=&& data-size=&normal& data-rawwidth=&1978& data-rawheight=&1358& class=&origin_image zh-lightbox-thumb& width=&1978& data-original=&http://pic1.zhimg.com/v2-ccf835edbad7bef5ba9c0_r.jpg&&&/figure&&p&你会看到外框为绿色的一个小方框。它称为&i&单元格&/i&。单元格是你编写和运行代码的地方。你也可以更改其类型,以呈现 Markdown(一种常用于编写 Web 内容的格式化语法)。&/p&&p&运行代码单元格时,单元格下方会显示输出。单元格还会被编号(左侧会显示 &code&In [1]:&/code&)。这能让你知道运行的代码和运行顺序(如果运行了多个单元格的话)。在 Markdown 模式下运行单元格会将 Markdown 呈现为文本。&/p&&p&&br&&/p&&p&可以把notebook看作有两种模式:一种是编辑模式,另一种是运行模式&/p&&p&编辑模式:我们可以在单元格内进行编辑&/p&&p&运行模式:就是编辑之后的代码或者是文档运行之后的模式,主要为了方便使用我们的一些快捷键,比如按F据可以全局搜索,按A就可以在当前行的的上面创建新的一行,按B则相反&/p&&p&编辑模式转到运行模式:可以运行enter+shift或者是正方形左边的那个按钮,或者是编辑到一半的时候想要退出,按一下ESC&/p&&p&运行模式转编辑模式:鼠标移动到对应的运行单元格点击一下就可以&br&&br&&b&工具栏&/b&&/p&&p&从左侧开始,工具栏上的其他控件是:&/p&&ul&&li&软盘符号表示“保存”。请记得保存 notebook!&/li&&li&&code&+&/code& 按钮用于创建新的单元格&/li&&li&然后是用于剪切、复制和粘贴单元格的按钮。&/li&&li&运行、停止、重新启动内核&/li&&li&单元格类型:代码、Markdown、原始文本和标题&/li&&li&命令面板(见下文)&/li&&li&单元格工具栏,提供不同的单元格选项(例如将单元格用作幻灯片)&/li&&/ul&&p&&b&代码单元格&/b&&/p&&p&notebook 中的大部分工作均在代码单元格中完成。这是编写和执行代码的地方。在代码单元格中可以执行多种操作,例如编写代码、给变量赋值、定义函数和类、导入包等。在一个单元格中执行的任何代码在所有其他单元格中均可用。&/p&&p&&br&&/p&&p&&b&Markdown 单元格&/b&&/p&&p&单元格也可用于以 Markdown 编写的文本。Markdown 是格式化语法,可加入链接、将文本样式设为粗体或斜体和设置代码格式。像代码单元格一样,按 &b&Shift + Enter&/b& 或 &b&Ctrl + Enter&/b& 可运行 Markdown 单元格,这会将 Markdown 呈现为格式化文本。加入文本可让你直接在代码旁写出叙述性文档,以及为代码和思路编写文档&/p&&p&标题&/p&&p&要编写标题,可在文本前放置井号,即 &code&#&/code&(英文读作 pound、hash 或 &a href=&http://link.zhihu.com/?target=http%3A//www.worldwidewords.org/weirdwords/ww-oct1.htm& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&octothorpe&/a&)。一个 &code&#&/code& 呈现为 &code&h1&/code& 标题,两个 &code&#&/code& 是 h2 标题,依此类推。#号越多字体越小&/p&&p&链接&/p&&h2&链接&/h2&&p&要在 Markdown 中添加链接,请在文本两侧加上方括号,并在 URL 两侧加上圆括号,例如[百度](&a href=&http://link.zhihu.com/?target=http%3A//baidu.com& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&baidu.com&/span&&span class=&invisible&&&/span&&/a&) 表示指向百度的链接。&/p&&p&代码&/p&&p&要创建代码块,请另起一行并用三个反撇号(一般在键盘数字 1 左边)将文本包起来:&/p&&p&```&/p&&p&import numpy&/p&&p&```&/p&&p&数学表达式&/p&&p&在 Markdown 单元格中,可以使用 &a href=&http://link.zhihu.com/?target=https%3A//www.latex-project.org/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&LaTeX&/a& 符号创建数学表达式。&/p&&p&notebook 使用 MathJax 将 LaTeX 符号呈现为数学符号。要启动数学模式,请在 LaTeX 符号两侧加上美元符号(例如 $y = mx + b$),以创建内联的数学表达式。对于数学符号块,请使用两个美元符号:&/p&&p&此功能的确很有用,因此,如果你没有用过 LaTeX,&a href=&http://link.zhihu.com/?target=http%3A//data-blog.udacity.com/posts/2016/10/latex-primer/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&请阅读这篇入门文档&/a&,它介绍了如何使用 LaTeX 来创建数学表达式。&/p&&a class=&video-box& href=&http://link.zhihu.com/?target=https%3A//www.zhihu.com/video/660544& target=&_blank& data-video-id=&& data-video-playable=&true& data-name=&& data-poster=&https://pic3.zhimg.com/80/v2-4825cef31bc0362bfea80ea2a9aa4082_b.jpg& data-lens-id=&660544&&
&img class=&thumbnail& src=&https://pic3.zhimg.com/80/v2-4825cef31bc0362bfea80ea2a9aa4082_b.jpg&&&span class=&content&&
&span class=&title&&&span class=&z-ico-extern-gray&&&/span&&span class=&z-ico-extern-blue&&&/span&&/span&
&span class=&url&&&span class=&z-ico-video&&&/span&https://www.zhihu.com/video/660544&/span&
&p&更多参考:&a href=&http://link.zhihu.com/?target=https%3A//jupyter.readthedocs.io/en/latest/architecture/how_jupyter_ipython_work.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&How IPython and Jupyter Notebook work&/a&&/p&
简单说说Anaconda和Jupyter有什么作用? 是一个包含数据科学常用包的 Python 发行版本。它基于 conda ——一个包和环境管理器——衍生而来。Anaconda 实际上是一个软件发行版,它附带了 conda、Python 和 150 多个科学包及其依赖项。conda是包和环…
&figure&&img src=&https://pic3.zhimg.com/v2-82db37a9c8cd91c7bf5a0aedcfe17395_b.jpg& data-rawwidth=&1163& data-rawheight=&346& class=&origin_image zh-lightbox-thumb& width=&1163& data-original=&https://pic3.zhimg.com/v2-82db37a9c8cd91c7bf5a0aedcfe17395_r.jpg&&&/figure&&h2&&b&一、创建类设计模式&/b&&/h2&&p&&br&&/p&&p&&b&前言&/b&&/p&&p&什么样的程序员是一个好的程序员?学会很多门编程语言,就是一个好的程序员了么?事实上,学会一门编程语言不是一件很难的事,而“学会”一门编程语言是非常难的一件事。前一个“会”强调“能”,懂语法,能写简单的程序就算是前者的“会”了;后一个“会”,强调“精”,显然,光能写出“Hello World”并不算是后者的“会”,能熟练应用,并用编程语言解决各种问题,才算是真正的“会”。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/14907/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&1、Python与设计模式--单例模式&/b&&/p&&p&总线是计算机各种功能部件或者设备之间传送数据、控制信号等信息的公共通信解决方案之一。现假设有如下场景:某中央处理器(CPU)通过某种协议总线与一个信号灯相连,信号灯有64种颜色可以设置,中央处理器上运行着三个线程,都可以对这个信号灯进行控制,并且可以独立设置该信号灯的颜色。抽象掉协议细节(用打印表示),如何实现线程对信号等的控制逻辑。&/p&&p&加线程锁进行控制,无疑是最先想到的方法,但各个线程对锁的控制,无疑加大了模块之间的耦合。下面,我们就用设计模式中的单例模式,来解决这个问题。&/p&&p&什么是单例模式?单例模式是指:保证一个类仅有一个实例,并提供一个访问它的全局访问点。具体到此例中,总线对象,就是一个单例,它仅有一个实例,各个线程对总线的访问只有一个全局访问点,即惟一的实例。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/14908/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&2、Python与设计模式--工厂类相关模式&/b&&/p&&p&想必大家一定见过类似于麦当劳自助点餐台一类的点餐系统吧。在一个大的触摸显示屏上,有三类可以选择的上餐品:汉堡等主餐、小食、饮料。当我们选择好自己需要的食物,支付完成后,订单就生成了。下面,我们用今天的主角--工厂模式--来生成这些食物的逻辑主体。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/14909/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&3、Python与设计模式--建造者模式&/b&&/p&&p&今天的例子,还是上一次谈到的快餐点餐系统。只不过,今天我们从订单的角度来构造这个系统。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/14910/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&4、Python与设计模式--原型模式&/b&&/p&&p&大家如果用过类似于Photoshop的平面设计软件,一定都知道图层的概念。图层概念的提出,使得设计、图形修改等操作更加便利。设计师既可以修改和绘制当前图像对象,又可以保留其它图像对象,逻辑清晰,且可以及时得到反馈。本节内容,将以图层为主角,介绍原型模式。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15164/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&h2&&b&二、结构类设计模式&/b&&/h2&&p&&br&&/p&&p&&b&1、Python与设计模式--代理模式&/b&&/p&&p&代理模式是一种使用频率非常高的模式,在多个著名的开源软件和当前多个著名的互联网产品后台程序中都有所应用。下面我们用一个抽象化的简单例子,来说明代理模式。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15329/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&2、Python与设计模式--装饰器模式&/b&&/p&&p&又提到了那个快餐点餐系统,不过今天我们只以其中的一个类作为主角:饮料类。&/p&&p&除了基本配置,快餐店卖可乐时,可以选择加冰,如果加冰的话,要在原价上加0.3元;卖牛奶时,可以选择加糖,如果加糖的话,要原价上加0.5元。怎么解决这样的问题?可以选择装饰器模式来解决这一类的问题。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15328/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&3、Python与设计模式--适配器模式&/b&&/p&&p&假设某公司A与某公司B需要合作,公司A需要访问公司B的人员信息,但公司A与公司B协议接口不同,该如何处理?&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36112/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&4、Python与设计模式--门面模式&/b&&/p&&p&门面模式也叫外观模式,定义如下:要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。门面模式注重“统一的对象”,也就是提供一个访问子系统的接口。门面模式与之前说过的模板模式有类似的地方,都是对一些需要重复方法的封装。但从本质上来说,是不同的。模板模式是对类本身的方法的封装,其被封装的方法也可以单独使用;而门面模式,是对子系统的封装,其被封装的接口理论上是不会被单独提出来用的。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36113/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&5、Python与设计模式--组合模式&/b&&/p&&p&组合模式也叫作部分-整体模式,其定义如下:将对象组合成树形结构以表示“部分”和“整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15167/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&6、Python与设计模式--享元模式&/b&&/p&&p&享元模式定义如下:使用共享对象支持大量细粒度对象。大量细粒度的对象的支持共享,可能会涉及这些对象的两类信息:内部状态信息和外部状态信息。内部状态信息就是可共享出来的信息,它们存储在享元对象内部,不会随着特定环境的改变而改变;外部状态信息就不可共享的信息了。享元模式中只包含内部状态信息,而不应该包含外部状态信息。这点在设计业务架构时,应该有所考虑。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15165/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&7、Python与设计模式--桥梁模式&/b&&/p&&p&桥梁模式又叫桥接模式,定义如下:将抽象与实现解耦(注意此处的抽象和实现,并非抽象类和实现类的那种关系,而是一种角色的关系,这里需要好好区分一下),可以使其独立变化。在形如上例中,Pen只负责画,但没有形状,它终究是不知道要画什么的,所以我们把它叫做抽象化角色;而Shape是具体的形状,我们把它叫做实现化角色。抽象化角色和实现化角色是解耦的,这也就意味着,所谓的桥,就是抽象化角色的抽象类和实现化角色的抽象类之间的引用关系。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15330/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&h2&&b&三、行为类设计模式&/b&&/h2&&p&&br&&/p&&p&&b&1、Python与设计模式--策略模式&/b&&/p&&p&假设某司维护着一些客户资料,需要在该司有新产品上市或者举行新活动时通知客户。现通知客户的方式有两种:短信通知、邮件通知。应如何设计该系统的客户通知部分?为解决该问题,我们先构造客户类,包括客户常用的联系方式和基本信息,同时也包括要发送的内容。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15331/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&2、Python与设计模式--责任链模式&/b&&/p&&p&假设有这么一个请假系统:员工若想要请3天以内(包括3天的假),只需要直属经理批准就可以了;如果想请3-7天,不仅需要直属经理批准,部门经理需要最终批准;如果请假大于7天,不光要前两个经理批准,也需要总经理最终批准。类似的系统相信大家都遇到过,那么该如何实现呢?&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15332/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&3、Python与设计模式--命令模式&/b&&/p&&p&又是一个点餐系统(原谅作者的吃货属性)。不过这次的点餐系统是个饭店的点餐系统。饭店的点餐系统有什么不同嘛?大伙想想看,在大多数饭店中,当服务员已经接到顾客的点单,录入到系统中后,根据不同的菜品,会有不同的后台反应。比如,饭店有凉菜间、热菜间、主食间,那当服务员将菜品录入到系统中后,凉菜间会打印出顾客所点的凉菜条目,热菜间会打印出顾客所点的热菜条目,主食间会打印出主食条目。那这个系统的后台模式该如何设计?&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15333/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&4、Python与设计模式--中介者模式&/b&&/p&&p&有一个手机仓储管理系统,使用者有三方:销售、仓库管理员、采购。需求是:销售一旦达成订单,销售人员会通过系统的销售子系统部分通知仓储子系统,仓储子系统会将可出仓手机数量减少,同时通知采购管理子系统当前销售订单;仓储子系统的库存到达阈值以下,会通知销售子系统和采购子系统,并督促采购子系统采购;采购完成后,采购人员会把采购信息填入采购子系统,采购子系统会通知销售子系统采购完成,并通知仓库子系统增加库存。&/p&&p&从需求描述来看,每个子系统都和其它子系统有所交流,在设计系统时,如果直接在一个子系统中集成对另两个子系统的操作,一是耦合太大,二是不易扩展。为解决这类问题,我们需要引入一个新的角色-中介者-来将“网状结构”精简为“星形结构”。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/15334/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&5、Python与设计模式--模板模式&/b&&/p&&p&投资股票是种常见的理财方式,我国股民越来越多,实时查询股票的需求也越来越大。今天,我们通过一个简单的股票查询客户端来认识一种简单的设计模式:模板模式。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36114/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&6、Python与设计模式--迭代器模式&/b&&/p&&p&今天的主角是迭代器模式。在python中,迭代器并不用举太多的例子,因为python中的迭代器应用实在太多了(不管是python还是其它很多的编程语言中,实际上迭代器都已经纳入到了常用的库或者包中)。而且在当前,也几乎没有人专门去开发一个迭代器,而是直接去使用list、string、set、dict等python可迭代对象,或者直接使用__iter__和next函数来实现迭代器。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36115/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&7、Python与设计模式--访问者模式&/b&&/p&&p&假设一个药房,有一些大夫,一个药品划价员和一个药房管理员,它们通过一个药房管理系统组织工作流程。大夫开出药方后,药品划价员确定药品是否正常,价格是否正确;通过后药房管理员进行开药处理。该系统可以如何实现?最简单的想法,是分别用一个一个if…else…把划价员处理流程和药房管理流程实现,这样做的问题在于,扩展性不强,而且单一性不强,一旦有新药的加入或者划价流程、开药流程有些变动,会牵扯比较多的改动。今天介绍一种解决这类问题的模式:访问者模式。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36116/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&8、Python与设计模式--观察者模式&/b&&/p&&p&在门面模式中,我们提到过火警报警器。在当时,我们关注的是通过封装减少代码重复。而今天,我们将从业务流程的实现角度,来再次实现该火警报警器。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36117/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&9、Python与设计模式--解释器模式&/b&&/p&&p&要开发一个自动识别谱子的吉他模拟器,达到录入谱即可按照谱发声的效果。除了发声设备外(假设已完成),最重要的就是读谱和译谱能力了。分析其需求,整个过程大致上分可以分为两部分:根据规则翻译谱的内容;根据翻译的内容演奏。我们用一个解释器模型来完成这个功能。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36118/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&10、Python与设计模式--备忘录模式&/b&&/p&&p&打过游戏的朋友一定知道,大多数游戏都有保存进度的功能,如果一局游戏下来,忘保存了进度,那么下次只能从上次进度点开始重新打了。一般情况下,保存进度是要存在可持久化存储器上,本例中先以保存在内存中来模拟实现该场景的情形。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36119/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&11、Python与设计模式--状态模式&/b&&/p&&p&电梯在我们周边随处可见,电梯的控制逻辑中心是由电梯控制器实现的。电梯的控制逻辑,即使简单点设计,把状态分成开门状态,停止状态和运行状态,操作分成开门、关门、运行、停止,那流程也是很复杂的。首先,开门状态不能开门、运行、停止;停止状态不能关门,停止;运行状态不能开门、关门、运行。要用一个一个if…else…实现,首先代码混乱,不易维护;二是不易扩展。至于各种设计原则什么的……&/p&&p&那该如何实现?在上边的逻辑中,每个操作仅仅是一个操作,状态切换与操作是分离的,这也造成后来操作和状态“相互配合”的“手忙脚乱”。如果把状态抽象成一个类,每个状态为一个子类,每个状态实现什么操作,不实现什么操作,仅仅在这个类中具体实现就可以了。&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//click.aliyun.com/m/36120/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&点击阅读详情&/a&&/p&&p&&br&&/p&&p&&b&更多技术干货敬请关注云栖社区知乎机构号:&a href=&https://www.zhihu.com/org/a-li-yun-yun-qi-she-qu-48& class=&internal&&阿里云云栖社区 - 知乎&/a&&/b&&/p&&p&&/p&&p&&/p&
一、创建类设计模式 前言什么样的程序员是一个好的程序员?学会很多门编程语言,就是一个好的程序员了么?事实上,学会一门编程语言不是一件很难的事,而“学会”一门编程语言是非常难的一件事。前一个“会”强调“能”,懂语法,能写简单的程序就算是前者…
&h2&1. 前言&/h2&&p&本文将基于&a href=&https://link.zhihu.com/?target=https%3A//github.com/pallets/flask/blob/0.1/flask.py& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&flask 0.1&/a&版本(git checkout 8605cc3)来分析flask的实现,试图理清flask中的一些概念,加深读者对flask的理解,提高对flask的认识。从而,在使用flask过程中,能够减少困惑,胸有成竹,遇bug而不惊。&/p&&figure&&img src=&https://pic3.zhimg.com/v2-49fa738badbf3b0da52dbece_b.jpg& data-rawwidth=&419& data-rawheight=&164& class=&content_image& width=&419&&&/figure&&br&&p&在试图理解flask的设计之前,你知道应该知道以下几个概念:&/p&&ul&&li&flask(web框架)是什么&/li&&li&WSGI是什么&/li&&li&jinja2是什么&/li&&li&Werkzeug是什么&/li&&/ul&&p&本文将首先回答这些问题,然后再分析flask源码。&/p&&h2&2. 知识准备&/h2&&h3&2.1 WSGI&/h3&&p&下面这张图来自&a href=&https://link.zhihu.com/?target=http%3A//hackerxu.com//flask011.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&,通过这张图,读者对web框架所处的位置和WSGI协议能够有一个感性的认识。&/p&&figure&&img src=&https://pic4.zhimg.com/v2-dc923ff0ca1d62e49437bb_b.jpg& data-rawwidth=&661& data-rawheight=&530& class=&origin_image zh-lightbox-thumb& width=&661& data-original=&https://pic4.zhimg.com/v2-dc923ff0ca1d62e49437bb_r.jpg&&&/figure&&br&&p&&strong&WSGI&/strong&&/p&&p&&a href=&https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/web_Server_Gateway_Interface& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&wikipedia&/a&上对WSGI的解释就比较通俗易懂。为了更好的理解WSGI,我们来看一个&a href=&https://link.zhihu.com/?target=http%3A//eventlet.net/doc/modules/wsgi.html%23eventlet.wsgi.server& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&例子&/a&:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&eventlet&/span& &span class=&kn&&import&/span& &span class=&n&&wsgi&/span&
&span class=&kn&&import&/span& &span class=&nn&&eventlet&/span&
&span class=&k&&def&/span& &span class=&nf&&hello_world&/span&&span class=&p&&(&/span&&span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&):&/span&
&span class=&n&&start_response&/span&&span class=&p&&(&/span&&span class=&s1&&'200 OK'&/span&&span class=&p&&,&/span& &span class=&p&&[(&/span&&span class=&s1&&'Content-Type'&/span&&span class=&p&&,&/span& &span class=&s1&&'text/plain'&/span&&span class=&p&&)])&/span&
&span class=&k&&return&/span& &span class=&p&&[&/span&&span class=&s1&&'Hello, World!&/span&&span class=&se&&\r\n&/span&&span class=&s1&&'&/span&&span class=&p&&]&/span&
&span class=&n&&wsgi&/span&&span class=&o&&.&/span&&span class=&n&&server&/span&&span class=&p&&(&/span&&span class=&n&&eventlet&/span&&span class=&o&&.&/span&&span class=&n&&listen&/span&&span class=&p&&((&/span&&span class=&s1&&''&/span&&span class=&p&&,&/span& &span class=&mi&&8090&/span&&span class=&p&&)),&/span& &span class=&n&&hello_world&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&我们定义了一个hello_world函数,这个函数接受两个参数。分别是environ和start_response,我们将这个hello_world传递给eventlet.wsgi.server以后, eventlet.wsgi.server在调用hello_world时,会自动传入environ和start_response这两个参数,并接受hello_world的返回值。而这,就是WSGI的作用。&/p&&p&也就是说,在python的世界里,通过WSGI约定了web服务器怎么调用web应用程序的代码,web应用程序需要符合什么样的规范,只要web应用程序和web服务器都遵守WSGI 协议,那么,web应用程序和web服务器就可以随意的组合。这也就是WSGI存在的原因。&/p&&p&WSGI是一种协议,这里,需要注意两个相近的概念:&/p&&ul&&li&uwsgi同WSGI一样是一种协议&/li&&li&而uWSGI是实现了uwsgi和WSGI两种协议的web服务器&/li&&/ul&&h3&2.2 jinja2与Werkzeug&/h3&&p&flask依赖jinja2和Werkzeug,为了完全理解flask,我们还需要简单介绍一下这两个依赖。&/p&&p&&strong&jinja2&/strong&&/p&&p&&a href=&https://link.zhihu.com/?target=http%3A//docs.jinkan.org/docs/jinja2/index.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Jinja2&/a&是一个功能齐全的模板引擎。它有完整的unicode支持,一个可选 的集成沙箱执行环境,被广泛使用。&/p&&p&jinja2的一个简单示例如下:&/p&&br&&div class=&highlight&&&pre&&code class=&language-django&&&span&&/span&&span class=&x&&&&& from jinja2 import Template&/span&
&span class=&x&&&&& template = Template('Hello !')&/span&
&span class=&x&&&&& template.render(name='John Doe')&/span&
&span class=&x&&u'Hello John Doe!'&/span&
&/code&&/pre&&/div&&p&&strong&Werkzeug&/strong&&/p&&p&Werkzeug是一个WSGI工具包,它可以作为web框架的底层库。&/p&&p&我发现Werkzeug的官方文档介绍特别好,下面这一段摘录自&a href=&https://link.zhihu.com/?target=http%3A//werkzeug-docs-cn.readthedocs.io/zh_CN/latest/tutorial.html%23id1& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&这里&/a&。&/p&&p&Werkzeug是一个WSGI工具包。WSGI是一个web应用和服务器通信的协议,web应用可以通过WSGI一起工作。一个基本的”Hello World”WSGI应用看起来是这样的:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&def&/span& &span class=&nf&&application&/span&&span class=&p&&(&/span&&span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&):&/span&
&span class=&n&&start_response&/span&&span class=&p&&(&/span&&span class=&s1&&'200 OK'&/span&&span class=&p&&,&/span& &span class=&p&&[(&/span&&span class=&s1&&'Content-Type'&/span&&span class=&p&&,&/span& &span class=&s1&&'text/plain'&/span&&span class=&p&&)])&/span&
&span class=&k&&return&/span& &span class=&p&&[&/span&&span class=&s1&&'Hello World!'&/span&&span class=&p&&]&/span&
&/code&&/pre&&/div&&p&上面这小段代码就是WSGI协议的约定,它有一个可调用的start_response 。environ包含了所有进来的信息。 start_response用来表明已经收到一个响应。 通过Werkzeug,我们可以不必直接处理请求或者响应这些底层的东西,它已经为我们封装好了这些。&/p&&p&请求数据需要environ对象,Werkzeug允许我们以一个轻松的方式访问数据。响应对象是一个WSGI应用,提供了更好的方法来创建响应。如下所示:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&werkzeug.wrappers&/span& &span class=&kn&&import&/span& &span class=&n&&Response&/span&
&span class=&k&&def&/span& &span class=&nf&&application&/span&&span class=&p&&(&/span&&span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&):&/span&
&span class=&n&&response&/span& &span class=&o&&=&/span& &span class=&n&&Response&/span&&span class=&p&&(&/span&&span class=&s1&&'Hello World!'&/span&&span class=&p&&,&/span& &span class=&n&&mimetype&/span&&span class=&o&&=&/span&&span class=&s1&&'text/plain'&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&response&/span&&span class=&p&&(&/span&&span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&h2&2.3 如何理解wsgi, Werkzeug, flask之间的关系&/h2&&p&Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug,它只是工具包,其用于接收http请求并对请求进行预处理,然后触发Flask框架,开发人员基于Flask框架提供的功能对请求进行相应的处理,并返回给用户,如果要返回给用户复杂的内容时,需要借助jinja2模板来实现对模板的处理。将模板和数据进行渲染,将渲染后的字符串返回给用户浏览器。&/p&&h2&2.4 Flask是什么,不是什么&/h2&&p&&a href=&https://link.zhihu.com/?target=https%3A//dormousehole.readthedocs.io/en/latest/design.html& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Flask&/a&永远不会包含数据库层,也不会有表单库或是这个方面的其它东西。Flask本身只是Werkzeug和Jinja2的之间的桥梁,前者实现一个合适的WSGI应用,后者处理模板。当然,Flask也绑定了一些通用的标准库包,比如logging。除此之外其它所有一切都交给扩展来实现。&/p&&p&为什么呢?因为人们有不同的偏好和需求,Flask不可能把所有的需求都囊括在核心里。大多数web应用会需要一个模板引擎。然而不是每个应用都需要一个SQL数据库的。&/p&&p&Flask 的理念是为所有应用建立一个良好的基础,其余的一切都取决于你自己或者 扩展。&/p&&h2&3. Flask源码分析&/h2&&p&Flask的使用非常简单,官网的例子如下:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&flask&/span& &span class=&kn&&import&/span& &span class=&n&&Flask&/span&
&span class=&n&&app&/span& &span class=&o&&=&/span& &span class=&n&&Flask&/span&&span class=&p&&(&/span&&span class=&n&&__name__&/span&&span class=&p&&)&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s2&&&/&&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&hello&/span&&span class=&p&&():&/span&
&span class=&k&&return&/span& &span class=&s2&&&Hello World!&&/span&
&span class=&k&&if&/span& &span class=&n&&__name__&/span& &span class=&o&&==&/span& &span class=&s2&&&__main__&&/span&&span class=&p&&:&/span&
&span class=&n&&app&/span&&span class=&o&&.&/span&&span class=&n&&run&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&&p&每当我们需要创建一个flask应用时,我们都会创建一个Flask对象:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&app&/span& &span class=&o&&=&/span& &span class=&n&&Flask&/span&&span class=&p&&(&/span&&span class=&n&&__name__&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&下面看一下Flask对象的__init__方法,如果不考虑jinja2相关,核心成员就下面几个:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&class&/span& &span class=&nc&&Flask&/span&&span class=&p&&:&/span&
&span class=&k&&def&/span& &span class=&nf&&__init__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&package_name&/span&&span class=&p&&):&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&package_name&/span& &span class=&o&&=&/span& &span class=&n&&package_name&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&root_path&/span& &span class=&o&&=&/span& &span class=&n&&_get_package_path&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&package_name&/span&&span class=&p&&)&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&view_functions&/span& &span class=&o&&=&/span& &span class=&p&&{}&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&error_handlers&/span& &span class=&o&&=&/span& &span class=&p&&{}&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&before_request_funcs&/span& &span class=&o&&=&/span& &span class=&p&&[]&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&after_request_funcs&/span& &span class=&o&&=&/span& &span class=&p&&[]&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&url_map&/span& &span class=&o&&=&/span& &span class=&n&&Map&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&&p&我们把目光聚集到后面几个成员,view_functions中保存了视图函数(处理用户请求的函数,如上面的hello()),error_handlers中保存了错误处理函数,before_request_funcs和after_request_funcs保存了请求的预处理函数和后处理函数。&/p&&p&self.url_map用以保存URI到视图函数的映射,即保存app.route()这个装饰器的信息,如下所示:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&def&/span& &span class=&nf&&route&/span&&span class=&p&&(&/span&&span class=&o&&...&/span&&span class=&p&&):&/span&
&span class=&k&&def&/span& &span class=&nf&&decorator&/span&&span class=&p&&(&/span&&span class=&n&&f&/span&&span class=&p&&):&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&add_url_rule&/span&&span class=&p&&(&/span&&span class=&n&&rule&/span&&span class=&p&&,&/span& &span class=&n&&f&/span&&span class=&o&&.&/span&&span class=&n&&__name__&/span&&span class=&p&&,&/span& &span class=&o&&**&/span&&span class=&n&&options&/span&&span class=&p&&)&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&view_functions&/span&&span class=&p&&[&/span&&span class=&n&&f&/span&&span class=&o&&.&/span&&span class=&n&&__name__&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&f&/span&
&span class=&k&&return&/span& &span class=&n&&f&/span&
&span class=&k&&return&/span& &span class=&n&&decorator&/span&
&/code&&/pre&&/div&&p&上面说到的是初始化部分,下面看一下执行部分,当我们执行app.run()时,调用堆栈如下:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&app&/span&&span class=&o&&.&/span&&span class=&n&&run&/span&&span class=&p&&()&/span&
&span class=&n&&run_simple&/span&&span class=&p&&(&/span&&span class=&n&&host&/span&&span class=&p&&,&/span& &span class=&n&&port&/span&&span class=&p&&,&/span& &span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&o&&**&/span&&span class=&n&&options&/span&&span class=&p&&)&/span&
&span class=&n&&__call__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&)&/span&
&span class=&n&&wsgi_app&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&wsgi_app是flask核心:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&def&/span& &span class=&nf&&wsgi_app&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&):&/span&
&span class=&k&&with&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&request_context&/span&&span class=&p&&(&/span&&span class=&n&&environ&/span&&span class=&p&&):&/span&
&span class=&n&&rv&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&preprocess_request&/span&&span class=&p&&()&/span&
&span class=&k&&if&/span& &span class=&n&&rv&/span& &span class=&ow&&is&/span& &span class=&bp&&None&/span&&span class=&p&&:&/span&
&span class=&n&&rv&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&dispatch_request&/span&&span class=&p&&()&/span&
&span class=&n&&response&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&make_response&/span&&span class=&p&&(&/span&&span class=&n&&rv&/span&&span class=&p&&)&/span&
&span class=&n&&response&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&process_response&/span&&span class=&p&&(&/span&&span class=&n&&response&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&response&/span&&span class=&p&&(&/span&&span class=&n&&environ&/span&&span class=&p&&,&/span& &span class=&n&&start_response&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&可以看到,wsgi_app这个函数的作用就是先调用所有的预处理函数,然后分发请求,再调用所有后处理函数,最后返回response。&/p&&p&看一下dispatch_request函数的实现,因为,这里有flask的错误处理逻辑:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&def&/span& &span class=&nf&&dispatch_request&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&):&/span&
&span class=&k&&try&/span&&span class=&p&&:&/span&
&span class=&n&&endpoint&/span&&span class=&p&&,&/span& &span class=&n&&values&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&match_request&/span&&span class=&p&&()&/span&
&span class=&k&&return&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&view_functions&/span&&span class=&p&&[&/span&&span class=&n&&endpoint&/span&&span class=&p&&](&/span&&span class=&o&&**&/span&&span class=&n&&values&/span&&span class=&p&&)&/span&
&span class=&k&&except&/span& &span class=&n&&HTTPException&/span&&span class=&p&&,&/span& &span class=&n&&e&/span&&span class=&p&&:&/span&
&span class=&n&&handler&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&error_handlers&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&n&&e&/span&&span class=&o&&.&/span&&span class=&n&&code&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&handler&/span& &span class=&ow&&is&/span& &span class=&bp&&None&/span&&span class=&p&&:&/span&
&span class=&k&&return&/span& &span class=&n&&e&/span&
&span class=&k&&return&/span& &span class=&n&&handler&/span&&span class=&p&&(&/span&&span class=&n&&e&/span&&span class=&p&&)&/span&
&span class=&k&&except&/span& &span class=&ne&&Exception&/span&&span class=&p&&,&/span& &span class=&n&&e&/span&&span class=&p&&:&/span&
&span class=&n&&handler&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&error_handlers&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&mi&&500&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&debug&/span& &span class=&ow&&or&/span& &span class=&n&&handler&/span& &span class=&ow&&is&/span& &span class=&bp&&None&/span&&span class=&p&&:&/span&
&span class=&k&&raise&/span&
&span class=&k&&return&/span& &span class=&n&&handler&/span&&span class=&p&&(&/span&&span class=&n&&e&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&如果出现错误,则根据相应的error code,调用不同的错误处理函数。&/p&&p&上面这段简单的源码分析,就已经将Flask几个核心变量和核心函数串联起来了。其实,我们这里扣出来的几段代码,也就是Flask的核心代码。毕竟,Flask的0.1版本包含大量注释以后,也才六百行代码。&/p&&h2&4. flask的魔法&/h2&&p&如果读者打开flask.py文件,将看到我前面的源码分析几乎已经覆盖了所有重要的代码。但是,细心的读者会看到,在Flask.py文件的末尾处,有以下几行代码:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&c1&&# context locals&/span&
&span class=&n&&_request_ctx_stack&/span& &span class=&o&&=&/span& &span class=&n&&LocalStack&/span&&span class=&p&&()&/span&
&span class=&n&&current_app&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&app&/span&&span class=&p&&)&/span&
&span class=&n&&request&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&request&/span&&span class=&p&&)&/span&
&span class=&n&&session&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&session&/span&&span class=&p&&)&/span&
&span class=&n&&g&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&g&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&这是我们得以方便的使用flask开发的魔法,也是flask源码中的难点。在分析之前,我们先看一下它们的作用。&/p&&p&在flask的开发过程中,我们可以通过如下方式访问url中的参数:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&kn&&from&/span& &span class=&nn&&flask&/span& &span class=&kn&&import&/span& &span class=&n&&request&/span&
&span class=&nd&&@app.route&/span&&span class=&p&&(&/span&&span class=&s1&&'/'&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&hello&/span&&span class=&p&&():&/span&
&span class=&n&&name&/span& &span class=&o&&=&/span& &span class=&n&&request&/span&&span class=&o&&.&/span&&span class=&n&&args&/span&&span class=&o&&.&/span&&span class=&n&&get&/span&&span class=&p&&(&/span&&span class=&s1&&'name'&/span&&span class=&p&&,&/span& &span class=&bp&&None&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&看起来request像是一个全局变量,那么,一个全局变量为什么可以在一个多线程环境中随意使用呢,下面就随我来一探究竟吧!&/p&&p&先看一下全局变量_request_ctx_stack的定义:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&_request_ctx_stack&/span& &span class=&o&&=&/span& &span class=&n&&LocalStack&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&&p&正如它LocalStack()的名字所暗示的那样,_request_ctx_stack是一个栈。显然,一个栈肯定会有push 、pop和top函数,如下所示:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&class&/span& &span class=&nc&&LocalStack&/span&&span class=&p&&(&/span&&span class=&nb&&object&/span&&span class=&p&&):&/span&
&span class=&k&&def&/span& &span class=&nf&&__init__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&):&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&_local&/span& &span class=&o&&=&/span& &span class=&n&&Local&/span&&span class=&p&&()&/span&
&span class=&k&&def&/span& &span class=&nf&&push&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&obj&/span&&span class=&p&&):&/span&
&span class=&n&&rv&/span& &span class=&o&&=&/span& &span class=&nb&&getattr&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&_local&/span&&span class=&p&&,&/span& &span class=&s1&&'stack'&/span&&span class=&p&&,&/span& &span class=&bp&&None&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&rv&/span& &span class=&ow&&is&/span& &span class=&bp&&None&/span&&span class=&p&&:&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&_local&/span&&span class=&o&&.&/span&&span class=&n&&stack&/span& &span class=&o&&=&/span& &span class=&n&&rv&/span& &span class=&o&&=&/span& &span class=&p&&[]&/span&
&span class=&n&&rv&/span&&span class=&o&&.&/span&&span class=&n&&append&/span&&span class=&p&&(&/span&&span class=&n&&obj&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&rv&/span&
&span class=&k&&def&/span& &span class=&nf&&pop&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&):&/span&
&span class=&n&&stack&/span& &span class=&o&&=&/span& &span class=&nb&&getattr&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&_local&/span&&span class=&p&&,&/span& &span class=&s1&&'stack'&/span&&span class=&p&&,&/span& &span class=&bp&&None&/span&&span class=&p&&)&/span&
&span class=&k&&if&/span& &span class=&n&&stack&/span& &span class=&ow&&is&/span& &span class=&bp&&None&/span&&span class=&p&&:&/span&
&span class=&k&&return&/span& &span class=&bp&&None&/span&
&span class=&k&&elif&/span& &span class=&nb&&len&/span&&span class=&p&&(&/span&&span class=&n&&stack&/span&&span class=&p&&)&/span& &span class=&o&&==&/span& &span class=&mi&&1&/span&&span class=&p&&:&/span&
&span class=&n&&release_local&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&_local&/span&&span class=&p&&)&/span&
&span class=&k&&return&/span& &span class=&n&&stack&/span&&span class=&p&&[&/span&&span class=&o&&-&/span&&span class=&mi&&1&/span&&span class=&p&&]&/span&
&span class=&k&&else&/span&&span class=&p&&:&/span&
&span class=&k&&return&/span& &span class=&n&&stack&/span&&span class=&o&&.&/span&&span class=&n&&pop&/span&&span class=&p&&()&/span&
&/code&&/pre&&/div&&p&按照我们的理解,要实现一个栈,那么LocalStack类应该有一个成员变量,是一个list,然后通过 这个list来保存栈的元素。然而,LocalStack并没有一个类型是list的成员变量, LocalStack仅有一个成员变量self._local = Local()。&/p&&p&顺藤摸瓜,我们来到了Werkzeug的源码中,到达了Local类的定义处:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&class&/span& &span class=&nc&&Local&/span&&span class=&p&&(&/span&&span class=&nb&&object&/span&&span class=&p&&):&/span&
&span class=&k&&def&/span& &span class=&nf&&__init__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&):&/span&
&span class=&nb&&object&/span&&span class=&o&&.&/span&&span class=&n&&__setattr__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&s1&&'__storage__'&/span&&span class=&p&&,&/span& &span class=&p&&{})&/span&
&span class=&nb&&object&/span&&span class=&o&&.&/span&&span class=&n&&__setattr__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&s1&&'__ident_func__'&/span&&span class=&p&&,&/span& &span class=&n&&get_ident&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&__getattr__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&name&/span&&span class=&p&&):&/span&
&span class=&k&&try&/span&&span class=&p&&:&/span&
&span class=&k&&return&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&__storage__&/span&&span class=&p&&[&/span&&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&__ident_func__&/span&&span class=&p&&()][&/span&&span class=&n&&name&/span&&span class=&p&&]&/span&
&span class=&k&&except&/span& &span class=&ne&&KeyError&/span&&span class=&p&&:&/span&
&span class=&k&&raise&/span& &span class=&ne&&AttributeError&/span&&span class=&p&&(&/span&&span class=&n&&name&/span&&span class=&p&&)&/span&
&span class=&k&&def&/span& &span class=&nf&&__setattr__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&name&/span&&span class=&p&&,&/span& &span class=&n&&value&/span&&span class=&p&&):&/span&
&span class=&n&&ident&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&__ident_func__&/span&&span class=&p&&()&/span&
&span class=&n&&storage&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&__storage__&/span&
&span class=&k&&try&/span&&span class=&p&&:&/span&
&span class=&n&&storage&/span&&span class=&p&&[&/span&&span class=&n&&ident&/span&&span class=&p&&][&/span&&span class=&n&&name&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&value&/span&
&span class=&k&&except&/span& &span class=&ne&&KeyError&/span&&span class=&p&&:&/span&
&span class=&n&&storage&/span&&span class=&p&&[&/span&&span class=&n&&ident&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&&span class=&n&&name&/span&&span class=&p&&:&/span& &span class=&n&&value&/span&&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&需要注意的是,Local类有两个成员变量,分别是__storage__和__ident_func__,其中,前者 是一个字典,后者是一个函数。这个函数的含义是,获取当前线程的id(或协程的id)。&/p&&p&此外,我们注意到,Local类自定义了__getattr__和__setattr__这两个方法,也就是说,我们在操作self.local.stack时, 会调用__setattr__和__getattr__方法。&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&_request_ctx_stack&/span& &span class=&o&&=&/span& &span class=&n&&LocalStack&/span&&span class=&p&&()&/span&
&span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&push&/span&&span class=&p&&(&/span&&span class=&n&&item&/span&&span class=&p&&)&/span&
&span class=&c1&&# 注意,这里赋值的时候,会调用__setattr__方法&/span&
&span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&_local&/span&&span class=&o&&.&/span&&span class=&n&&stack&/span& &span class=&o&&=&/span& &span class=&n&&rv&/span& &span class=&o&&=&/span& &span class=&p&&[]&/span& &span class=&o&&==&&/span& &span class=&n&&__setattr__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&name&/span&&span class=&p&&,&/span& &span class=&n&&value&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&而__setattr的定义如下:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&k&&def&/span& &span class=&nf&&__setattr__&/span&&span class=&p&&(&/span&&span class=&bp&&self&/span&&span class=&p&&,&/span& &span class=&n&&name&/span&&span class=&p&&,&/span& &span class=&n&&value&/span&&span class=&p&&):&/span&
&span class=&n&&ident&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&__ident_func__&/span&&span class=&p&&()&/span&
&span class=&n&&storage&/span& &span class=&o&&=&/span& &span class=&bp&&self&/span&&span class=&o&&.&/span&&span class=&n&&__storage__&/span&
&span class=&k&&try&/span&&span class=&p&&:&/span&
&span class=&n&&storage&/span&&span class=&p&&[&/span&&span class=&n&&ident&/span&&span class=&p&&][&/span&&span class=&n&&name&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&n&&value&/span&
&span class=&k&&except&/span& &span class=&ne&&KeyError&/span&&span class=&p&&:&/span&
&span class=&n&&storage&/span&&span class=&p&&[&/span&&span class=&n&&ident&/span&&span class=&p&&]&/span& &span class=&o&&=&/span& &span class=&p&&{&/span&&span class=&n&&name&/span&&span class=&p&&:&/span& &span class=&n&&value&/span&&span class=&p&&}&/span&
&/code&&/pre&&/div&&p&在__setattr__中,通过__ident_func__获取到了一个key,然后进行赋值。自此,我们可以知道, LocalStack是一个全局字典,或者说是一个名字空间。这个名字空间是所有线程共享的。 当我们访问字典中的某个元素的时候,会通过__getattr__进行访问,__getattr__先通过线程id, 找当前这个线程的数据,然后进行访问。&/p&&p&字段的内容如下:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&p&&{&/span&&span class=&s1&&'thread_id'&/span&&span class=&p&&:{&/span&&span class=&s1&&'stack'&/span&&span class=&p&&:[]}}&/span&
&span class=&p&&{&/span&&span class=&s1&&'thread_id1'&/span&&span class=&p&&:{&/span&&span class=&s1&&'stack'&/span&&span class=&p&&:[&/span&&span class=&n&&_RequestContext&/span&&span class=&p&&()]},&/span&
&span class=&s1&&'thread_id2'&/span&&span class=&p&&:{&/span&&span class=&s1&&'stack'&/span&&span class=&p&&:[&/span&&span class=&n&&_RequestContext&/span&&span class=&p&&()]}}&/span&
&/code&&/pre&&/div&&p&最后,我们来看一下其他几个全局变量:&/p&&br&&div class=&highlight&&&pre&&code class=&language-python&&&span&&/span&&span class=&n&&current_app&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&app&/span&&span class=&p&&)&/span&
&span class=&n&&request&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&request&/span&&span class=&p&&)&/span&
&span class=&n&&session&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&session&/span&&span class=&p&&)&/span&
&span class=&n&&g&/span& &span class=&o&&=&/span& &span class=&n&&LocalProxy&/span&&span class=&p&&(&/span&&span class=&k&&lambda&/span&&span class=&p&&:&/span& &span class=&n&&_request_ctx_stack&/span&&span class=&o&&.&/span&&span class=&n&&top&/span&&span class=&o&&.&/span&&span class=&n&&g&/span&&span class=&p&&)&/span&
&/code&&/pre&&/div&&p&读者可以自行看一下LocalProxy的源码,LocalProxy仅仅是一个代理(可以想象设计模式中的代理模式)。&/p&&p&通过LocalStack和LocalProxy这样的Python魔法,每个线程访问当前请求中的数据(request, session)时, 都好像都在访问一个全局变量,但是,互相之间又互不影响。这就是Flask为我们提供的便利,也是我们 选择Flask的理由!&/p&&h2&5. 总结&/h2&&p&在这篇文章中,我们简单地介绍了WSGI, jinja2和Werkzeug,详细介绍了Flask在web开发中所处的位置和发挥的作用。最后,深入Flask的源码,了解了Flask的实现。&/p&&h2&6. 参考资料&/h2&&ol&&li&&a href=&https://link.zhihu.com/?target=https%3A//en.wikipedia.org/wiki/Web_Server_Gateway_Interface& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Web Server Gateway Interface&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=http%3A//docs.jinkan.org/docs/jinja2/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&欢迎来到 Jinja2 - Jinja2 2.7 documentation&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=http%3A//werkzeug-docs-cn.readthedocs.io/zh_CN/latest/& class=& wrap external& target=&_blank& rel=&nofollow noreferrer&&Werkzeug 文档概览 - Werkzeug 0.9.4 文档&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=http%3A//www.zlovezl.cn/articles/charming-python-start-from-flask-request/& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://www.&/span&&span class=&visible&&zlovezl.cn/articles/cha&/span&&span class=&invisible&&rming-python-start-from-flask-request/&/span&&span class=&ellipsis&&&/span&&/a&&/li&&li&&a href=&https://link.zhihu.com/?target=http%3A//hackerxu.com//flask011.html& class=& external& target=&_blank& rel=&nofollow noreferrer&&&span class=&invisible&&http://&/span&&span class=&visible&&hackerxu.com/&/span&&span class=&invisible&&/flask011.html&/span&&span class=&ellipsis&&&/span&&/a&&/li&&/ol&
1. 前言本文将基于版本(git checkout 8605cc3)来分析flask的实现,试图理清flask中的一些概念,加深读者对flask的理解,提高对flask的认识。从而,在使用flask过程中,能够减少困惑,胸}

我要回帖

更多关于 ce修改器数值脚本 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信