CSS入门指南-3:定位元素

这是《CSS设计指南》的读书笔记,用于加深学习效果。 前一篇CSS入门指南-2:盒子模型、浮动和清除介绍了css盒子模型、浮动和清除,这一篇介绍 css元素的定位。 定位(position) CSS 布局的核心是 position 属性,对元素盒子应用这个属性,可以相对于它在常规文档流中的位置重新定位。 position 属性有4个值:static、relative、absoulte、fixed,默认值为 static。 接下来我会用以下四个段落来逐个说明这些属性是什么意思。 <p id="first">First Paragraph</p> <p id="Second">Second Paragraph</p> <p id="specialpara">Third Paragraph</p> <p id="fourth">First Paragraph</p> 静态定位(static) 我们先看一下四个段落都采用静态定位的效果。 静态定位下,每个元素在处在常规文档流中,它们都是块级元素,所以会在页面中自上而下地堆叠。 相对定位(relative) 现在我把第三段的 position 属性设置为 relative。 p#specialpara { position: relative; top: 25px; left: 30px; } 因为相对定位相对的是它原来在文档流中的位置(默认位置),所以如果只设置 position 样式不会有任何变化。这里我同时设置了 top 和 left 属性来改变它的位置。 现在它的效果如图所示: 现在,第三段从原来的元素(body)中挣脱了出来,与它在文档中的默认位置相比向下移动了25像素,向右移动了30像素。 需要注意的是,除了这个元素自己相对于原始位置挪动了一下以外,页面没有任何改变。这个元素原来占据的空间没有动,其他元素也没动。 这时,如果不想第四段被它挡住,可以给第四段设置一个 margin-top 值。 绝对定位(absoulte) 绝对定位跟静态定位和相对定位相比,它会把元素彻底从文档流中拿出来。 我们把 position 改为绝对定位看一下: p#specialpara { position: absoulte; top: 25px; left: 30px; } 效果如图: 可以看到,第三段原来的位置被回收了。这说明绝对定位的元素脱离了常规文档流,它现在是相对于顶级元素 body 在定位。 现在就涉及到一个概念:定位上下文,这个后边说,先继续看最后一种定位方式:固定定位。 盒子位移属性是如何工作? 盒子的位移属性有四个“top、right、bottom和left”,用来指定元素的定位位置和方向。这些属性只能在元素的“position”属性设置了“relative、absolute和fixed”属性值,才生效。 对于相对定位元素,这些属性的设置让元素从默认位置移动。例如,top设置一个值“20px”在一个相对定位的元素上,这个元素会在原来位置向下移动“20px”。 ...

2017-07-26 · 2 min · 290 words

理解JWT(JSON Web Token)认证及实践

最近想做个小程序,需要用到授权认证流程。以前项目都是用的 OAuth2 认证,但是Sanic 使用OAuth2 不太方便,就想试一下 JWT 的认证方式。 这一篇主要内容是 JWT 的认证原理,以及python 使用 jwt 认识的实践。 几种常用的认证机制 HUGOMORE42 HTTP Basic Auth HTTP Basic Auth 在HTTP中,基本认证是一种用来允许Web浏览器或其他客户端程序在请求时提供用户名和口令形式的身份凭证的一种登录验证方式,通常用户名和明码会通过HTTP头传递。 在发送之前是以用户名追加一个冒号然后串接上口令,并将得出的结果字符串再用Base64算法编码。例如,提供的用户名是Aladdin、口令是open sesame,则拼接后的结果就是Aladdin:open sesame,然后再将其用Base64编码,得到QWxhZGRpbjpvcGVuIHNlc2FtZQ==。最终将Base64编码的字符串发送出去,由接收者解码得到一个由冒号分隔的用户名和口令的字符串。 优点 基本认证的一个优点是基本上所有流行的网页浏览器都支持基本认证。 缺点 由于用户名和密码都是Base64编码的,而Base64编码是可逆的,所以用户名和密码可以认为是明文。所以只有在客户端和服务器主机之间的连接是安全可信的前提下才可以使用。 接下来我们看一个更加安全也适用范围更大的认证方式 OAuth。 OAuth OAuth 是一个关于授权(authorization)的开放网络标准。允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。现在的版本是2.0版。 严格来说,OAuth2不是一个标准协议,而是一个安全的授权框架。它详细描述了系统中不同角色、用户、服务前端应用(比如API),以及客户端(比如网站或移动App)之间怎么实现相互认证。 名词定义 Third-party application: 第三方应用程序,又称"客户端"(client) HTTP service:HTTP服务提供商 Resource Owner:资源所有者,通常称"用户"(user)。 User Agent:用户代理,比如浏览器。 Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。 Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。 OAuth 2.0 运行流程如图: (A)用户打开客户端以后,客户端要求用户给予授权。 (B)用户同意给予客户端授权。 (C)客户端使用上一步获得的授权,向认证服务器申请令牌。 (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。 (E)客户端使用令牌,向资源服务器申请获取资源。 (F)资源服务器确认令牌无误,同意向客户端开放资源。 优点 快速开发 实施代码量小 维护工作减少 如果设计的API要被不同的App使用,并且每个App使用的方式也不一样,使用OAuth2是个不错的选择。 缺点: OAuth2是一个安全框架,描述了在各种不同场景下,多个应用之间的授权问题。有海量的资料需要学习,要完全理解需要花费大量时间。 OAuth2不是一个严格的标准协议,因此在实施过程中更容易出错。 了解了以上两种方式后,现在终于到了本篇的重点,JWT 认证。 ...

2017-07-23 · 2 min · 398 words

CSS入门指南-2:盒子模型、浮动和清除

这是CSS设计指南的读书笔记,用于加深学习效果。 上一篇介绍了css 的工作原理,这一篇主要介绍盒子模型和浮动。 盒子模型 所谓盒子模型,就是浏览器为页面中的每个HTML元素生成的矩形盒子。这些盒子们都要按照可见版式模型在页面上排布。 HUGOMORE42 可见的页面版式主要由三个属性控制:position、display和float。 position:控制页面上元素的位置关系 display:控制元素是堆叠、并排还是不在页面出现 float:提供控制的方式,以便吧元素组成多栏布局 元素盒子的属性可以分成三组: 边框(board)。可以甚至边框的宽窄、样式和颜色 内边距(padding)。可以甚至盒子内容区与边框的间距 外边距(margin)。可以设置盒子与相邻元素的间距 元素盒子还有一个背景层,可以改变颜色,也可以添加图片。 简写样式 CSS为边框、内边距和外边距分别规定了简写属性,每个简写声明中,属性值得顺序都是上、右、下、左。 比如: { margin-top: 5px; margin-right: 10px; margin-bottom: 12px; margin-left: 8px; } 使用简写则为这样: { margin: 12px 10px 12px 8px; } 如果有一个值没写,那么则使用对边的值。 比如: {margin: 12px 10px 12px;} /*等同于*/ { margin: 12px 10px 12px 10px; } 如果只写一个值,则4个边都取这个值。 {margin: 12px;} /*等同于*/ {margin: 12px 12px 12px 12px;} 另外每个盒子的属性也分三个粒度,这三个粒度从一般到特殊分别举例如下: { border: 2px dashed red; } 混合使用三种粒度的简写属性达成设计目标是很常见的。比如,想为盒子的上边和下边添加4像素的红色边框,为左边添加1像素宽的红色边框,而右边没有。可以这么写: {border: 4px solid red;} /* 先给4条边设置相同的样式*/ {border-left-width: 1px;} /* 修改左边框宽度*/ {border-fight: none;} /*移出右边框*/ 盒子边框 border 有三个相关属性。 宽度(border-width)。可以使用thin、medium和thick等文本值,也可以使用除百分比和负值之外的任何绝对值。 样式(border-style)。有none、hidden、dotted、dashed等文本值。 颜色(border-color)。可以使用任意颜色值,包括RGB、HSL、十六进制颜色值和颜色关键字。 盒子内边距 内边距是盒子内容区与盒子边框之间的距离。 上图的样式为: p { font: 16px helvetica, sans-serif; width: 220px; border: 2px solid red; background-color: #caebff; } 可以看到在没有设定内边距的情况下,内容紧挨着边框。 设定边框后: ...

2017-07-19 · 3 min · 571 words

微信公号DIY:MongoDB 简易ORM & 公号记账数据库设计

前两篇 微信公号DIY 系列: 微信公号DIY:一小时搭建微信聊天机器人 微信公号DIY:训练聊天机器人&公号变身图片上传工具 介绍了如何使用搭建&训练聊天机器人以及让公号支持图片上传到七牛,把公号变成一个七牛图片上传客户端。这一篇将继续开发公号,让公号变成一个更加实用的工具账本(理财从记账开始)。 代码: 项目代码已上传至github,地址为gusibi/momo:https://github.com/gusibi/momo HUGOMORE42 账本功能 账本是一个功能比较简单应用,公号内只需要支持: 记账(记账,修改金额,取消记账) 账单统计(提供数据和图片形式的统计功能) 当然后台管理功能就比较多了,这个以后再介绍。 对于数据存储,我选择的是MongoDB(选MongoDB的原因是,之前没用过,想试一下),我们先看下MongoDB和关系型数据库的不同。 MongoDB 什么是MongoDB ? MongoDB 是由C++语言编写的,是一个开放源代码的面向文档的数据库,易于开发和缩放。 mongo和传统关系数据库的最本质的区别在那里呢?MongoDB 是文档模型。 关系模型和文档模型的区别在哪里? 关系模型需要你把一个数据对象,拆分成零部件,然后存到各个相应的表里,需要的是最后把它拼起来。举例子来说,假设我们要做一个CRM应用,那么要管理客户的基本信息,包括客户名字、地址、电话等。由于每个客户可能有多个电话,那么按照第三范式,我们会把电话号码用单独的一个表来存储,并在显示客户信息的时候通过关联把需要的信息取回来。 而MongoDB的文档模式,与这个模式大不相同。由于我们的存储单位是一个文档,可以支持数组和嵌套文档,所以很多时候你直接用一个这样的文档就可以涵盖这个客户相关的所有个人信息。关系型数据库的关联功能不一定就是它的优势,而是它能够工作的必要条件。 而在MongoDB里面,利用富文档的性质,很多时候,关联是个伪需求,可以通过合理建模来避免做关联。 MongoDB 概念解析 在mongodb中基本的概念是文档、集合、数据库,下表是MongoDB和关系型数据库概念对比: SQL术语/概念 MongoDB术语/概念 解释/说明 database database 数据库 table collection 数据库表/集合 row document 数据记录行/文档 column field 数据字段/域 index index 索引 table joins 表连接,MongoDB不支持 primary key primary key 主键,MongoDB自动将_id字段设置为主键 通过下图实例,我们也可以更直观的的了解Mongo中的一些概念: 接下来,我从使用的角度来介绍下如何使用 python 如何使用MongoDB,在这个过程中,我会实现一个简单的MongoDB的ORM,同时也会解释一下涉及到的概念。 简易 Python MongoDB ORM python 使用 mongodb 首先,需要确认已经安装了 PyMongo,如果没有安装,使用以下命令安装: pip install pymongo # 或者 easy_install pymongo 详细安装步骤参考: PyMongo Installing / Upgrading ...

2017-07-16 · 4 min · 659 words

使用swagger 生成 Flask RESTful API

什么是 RESTful 什么是REST REST(英文:Representational State Transfer,又称具象状态传输)是Roy Thomas Fielding博士于2000年在他的博士论文[1] 中提出来的一种万维网软件架构风格,目的是便于不同软件/程序在网络(例如互联网)中互相传递信息。 REST 的核心是可编辑的资源及其集合,用符合 Atom 文档标准的 Feed 和 Entry 表示。每个资源或者集合有一个惟一的 URI。系统以资源为中心,构建并提供一系列的 Web 服务。 在 REST 中,开发人员显式地使用 HTTP 方法,对系统资源进行创建、读取、更新和删除的操作: 使用 POST 方法在服务器上创建资源 使用 GET 方法从服务器检索某个资源或者资源集合 使用 PUT 方法对服务器的现有资源进行更新 使用 DELETE 方法删除服务器的某个资源 如果一个架构符合REST原则,就可以称它为RESTful架构。 RESTful API 设计定义 以下是几个RESTful API的几个概念。 资源(Resource):系统上的所有事物都被抽象为资源(一篇文章,一张照片,一段语音) 集合(Collection):一组资源的合辑称为集合(几篇文章,几张照片) 路径(Endpoint):路径又称”终点“,表示API的具体网址(每个网址代表一种资源) 那么一个设计良好的RESTful API应该遵循哪些原则呢? 协议 API与用户的通信协议总是使用HTTPs协议。 域名 应该尽量将API部署在专用域名,例如: https://apis.gusibi.com API地址和版本 在url中指定API版本。比如: https://apis.gusibi.com/v1 以资源为中心设计URL 资源是RESTful API的核心元素,所有的操作都是针对特定资源进化的。而资源就是URL表示的,所以简洁、清晰、结构化的URL设计是至关重要的。 在RESTful 架构中,每个网址代表一种资源(resource),所以网址中不能有动词,只能有名词,而且所用的名词往往与数据库的表格名对应。我们来看一下 Github 的例子: /users/:username/repos /users/:org/repos /repos/:owner/:repo /repos/:owner/:repo/tags /repos/:owner/:repo/branches/:branch 使用正确的Method 对于资源的具体操作类型,使用HTTP method 表示。 以下是常用的HTTP方法。 ...

2017-07-11 · 4 min · 778 words

微信公号DIY:训练聊天机器人&公号变身图片上传工具

上一篇 一小时搭建微信聊天机器人 介绍了如何搭建一个可用的聊天机器人,但是和机器人聊完你会发现,聊天机器人实在是太傻了,来回就那么几句。这是因为我们给聊天机器人的数据太少,他只能在我们给的训练集中找它认为最合适的。那么,如何导入更多的训练数据呢? 我能想到最简单的方法是找对话的数据,然后把这些数据作为训练数据训练机器人。 感谢 candlewill 已经收集好了大量的训练数据,dialog_corpus https://github.com/candlewill/Dialog_Corpus 。 HUGOMORE42 这个库中包含电影台词、中英文短信息、自然语言处理相关的数据集、小黄鸡语料等。这里我选择电影台词语料。 语料地址为:dgk_lost_conv:https://github.com/rustch3n/dgk_lost_conv chatterbot 训练逻辑处理模块 这个模块提供训练机器人的方法,chatterbot自带了通过输入list来训练([“你好”, “你好啊”] 后者是前者的回答)以及通过导入Corpus格式文件来训练的方式。 这里我们选择使用第一种,通过输入list来训练机器人。 处理训练数据 首先下载数据集: wget https://codeload.github.com/rustch3n/dgk_lost_conv/zip/master # 解压 $ unzip dgk_lost_conv-master.zip 我们先打开一个文件看下数据结构: E M 你得想想办法 我弟弟是无辜的 M 他可是美国公民啊 M 对此我也无能为力 M 你当然能 M 再去犯罪现场看看 定能证实清白 M 你看 我不过是个夜间办事员而已 M 你若真想解决问题 M 最好等领事来 M 他早上才上班 M 我很抱歉 E M 那我自己来搞定 M 你兄弟 M 关在哪个监狱? M 索纳监狱 E M 怎么了? M 那里关的都是最穷凶极恶的罪犯 M 别的监狱都不收 .conv 语料文件中:E 是分隔符 M 表示会话。因为我是使用输入list 的方式训练数据,这时我可以以分隔符E为分隔,将一段对话放入一个list中,那么上述例子中的训练数据应该被格式化为: ...

2017-07-08 · 3 min · 571 words

微信公号DIY:一小时搭建微信聊天机器人

最近借用了女朋友的公号,感觉如果只是用来发文章,太浪费微信给提供的这些功能了。想了想,先从最简单的开始,做一个聊天机器人吧。 使用Python实现聊天机器人的方案有多种:AIML、chatterBot以及图灵聊天机器人和微软小冰等。 考虑到以后可能会做一些定制化的需求,这里我选择了chatterBot(github 项目地址:https://github.com/gunthercox/ChatterBot)。 chatterbot是一款python接口的,基于一系列规则和机器学习算法完成的聊天机器人。具有结构清晰,可扩展性好,简单实用的特点。 HUGOMORE42 chatterBot 的工作流程如图: 输入模块(input adapter)从终端或者API等输入源获取数据 输入源会被指定的逻辑处理模块(logic Adapter)分别处理,逻辑处理模块会匹配训练集中已知的最接近输入数据句子A,然后根据句子A去找到相关度最高的结果B,如果有多个逻辑处理模块返回了不同的结果,会返回一个相关度最高的结果。 输出模块(output adapter)将匹配到的结果返回给终端或者API。 值得一说的是chatterBot 是一个模块化的项目,分为 input Adapter、logic Adapter、storage Adapter、output Adapter以及Trainer 模块。 logic Adapter是一个插件式设计,主进程在启动时会将用户定义的所有逻辑处理插件添加到logic context中,然后交MultiLogicAdapter 进行处理,MultiLogicAdapter 依次调用每个 logic Adapter,logic Adapter 被调用时先执行can_process 方式判断输入是否可以命中这个逻辑处理插件。比如”今天天气怎么样“这样的问题显然需要命中天气逻辑处理插件,这时时间逻辑处理插件的can_process 则会返回False。在命中后logic Adapter 负责计算出对应的回答(Statement对象)以及可信度(confidence),MultiLogicAdapter会取可信度最高的回答,并进入下一步。 下面我们来看下 chatterBot 如何使用 chatterBot 安装&使用 安装 chatterBot 是使用Python编写的,可以使用 pip 安装: pip install chatterbot chatterBot 的中文对话要求Python3 以上版本,建议在Python3.x 环境下开发 测试 打开iPython,输入测试一下 In [1]: from chatterbot import ChatBot # import ChatBot In [2]: momo = ChatBot('Momo', trainer='chatterbot.trainers.ChatterBotCorpusTrainer') /Users/gs/.virtualenvs/py3/lib/python3.6/site-packages/chatterbot/storage/jsonfile.py:26: UnsuitableForProductionWarning: The JsonFileStorageAdapter is not recommended for production environments. self.UnsuitableForProductionWarning # 这里storage adapter 默认使用的是 json 格式存储数据的,如果想在服务端部署,应该避免使用这种格式,因为实在是太慢了 In [3]: momo.train("chatterbot.corpus.chinese") # 指定训练集,这里我们使用中文 # 下边是对话结果 In [4]: momo.get_response('你好') Out[4]: <Statement text:你好> In [5]: momo.get_response('怎么了') Out[5]: <Statement text:没什么.> In [6]: momo.get_response('你知道它的所有内容吗?') Out[6]: <Statement text:优美胜于丑陋.> In [7]: momo.get_response('你是一个程序员吗?') Out[7]: <Statement text:我是个程序员> In [8]: momo.get_response('你使用什么语言呢?') Out[8]: <Statement text:我经常使用 Python, Java 和 C++ .> 这时你已经可以和机器人对话了,不过现在由于训练数据太少,机器人只能返回简单的对话。 ...

2017-07-05 · 5 min · 1035 words

python并发4:使用thread处理并发

这一篇是Python并发的第四篇,主要介绍进程和线程的定义,Python线程和全局解释器锁以及Python如何使用thread模块处理并发,这篇文章之前发过,但是前几篇介绍到了并发,就顺便再发一下组成一个系列 引言&动机 考虑一下这个场景,我们有10000条数据需要处理,处理每条数据需要花费1秒,但读取数据只需要0.1秒,每条数据互不干扰。该如何执行才能花费时间最短呢? 在多线程(MT)编程出现之前,电脑程序的运行由一个执行序列组成,执行序列按顺序在主机的中央处理器(CPU)中运行。无论是任务本身要求顺序执行还是整个程序是由多个子任务组成,程序都是按这种方式执行的。即使子任务相互独立,互相无关(即,一个子任务的结果不影响其它子 任务的结果)时也是这样。 HUGOMORE42 对于上边的问题,如果使用一个执行序列来完成,我们大约需要花费 10000*0.1 + 10000 = 11000 秒。这个时间显然是太长了。 那我们有没有可能在执行计算的同时取数据呢?或者是同时处理几条数据呢?如果可以,这样就能大幅提高任务的效率。这就是多线程编程的目的。 对于本质上就是异步的, 需要有多个并发事务,各个事务的运行顺序可以是不确定的,随机的,不可预测的问题,多线程是最理想的解决方案。这样的任务可以被分成多个执行流,每个流都有一个要完成的目标,然后将得到的结果合并,得到最终的结果。 线程和进程 什么是进程 进程(有时被称为重量级进程)是程序的一次 执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系 统管理在其上运行的所有进程,并为这些进程公平地分配时间。进程也可以通过 fork 和 spawn 操作 来完成其它的任务。不过各个进程有自己的内存空间,数据栈等,所以只能使用进程间通讯(IPC), 而不能直接共享信息。 什么是线程 线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中, 共享相同的运行环境。它们可以想像成是在主进程或“主线程”中并行运行的“迷你进程”。 线程状态如图 线程有开始,顺序执行和结束三部分。它有一个自己的指令指针,记录自己运行到什么地方。 线程的运行可能被抢占(中断),或暂时的被挂起(也叫睡眠),让其它的线程运行,这叫做让步。 一个进程中的各个线程之间共享同一片数据空间,所以线程之间可以比进程之间更方便地共享数据以及相互通讯。 当然,这样的共享并不是完全没有危险的。如果多个线程共同访问同一片数据,则由于数据访 问的顺序不一样,有可能导致数据结果的不一致的问题。这叫做竞态条件(race condition)。 线程一般都是并发执行的,不过在单 CPU 的系统中,真正的并发是不可能的,每个线程会被安排成每次只运行一小会,然后就把 CPU 让出来,让其它的线程去运行。由于有的函数会在完成之前阻塞住,在没有特别为多线程做修改的情 况下,这种“贪婪”的函数会让 CPU 的时间分配有所倾斜。导致各个线程分配到的运行时间可能不 尽相同,不尽公平。 Python、线程和全局解释器锁 全局解释器锁(GIL) 首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。就好比C++是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行(其中的JPython就没有GIL)。 那么CPython实现中的GIL又是什么呢?GIL全称Global Interpreter Lock为了避免误导,我们还是来看一下官方给出的解释: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.) ...

2017-07-02 · 8 min · 1517 words

python并发3:使用asyncio编写服务器

asyncio 上一篇我们介绍了 asyncio 包,以及如何使用异步编程管理网络应用中的高并发。在这一篇,我们主要介绍使用 asyncio 包编程的两个例子。 async/await语法 我们先介绍下 async/await 语法,要不然看完这篇可能会困惑,为什么之前使用 asyncio.coroutine 装饰器 和 yield from,这里都是 用的 async 和 await? python并发2:使用asyncio处理并发 async/await 是Python3.5 的新语法,语法如下: async def read_data(db): pass async 是明确将函数声明为协程的关键字,即使没有await表达式,函数执行也会返回一个协程对象。 在协程函数内部,可以在某个表达式之前使用 await 关键字来暂停协程的执行,以等待某协程完成: async def read_data(db): data = await db.fetch('SELECT ...') 这个代码如果使用 asyncio.coroutine 装饰器语法为: @asyncio.coroutine def read_data(db): data = yield from db.fetch('SELECT ...') 这两段代码执行的结果是一样的,也就是说 可以把 asyncio.coroutine 替换为 async, yield from 替换为 await。 使用新的语法有什么好处呢: 使生成器和协程的概念更容易理解,因为语法不同 可以消除由于重构时不小心移出协程中yield 声明而导致的不明确错误,这回导致协程变成普通的生成器。 使用 asyncio 包编写服务器 这个例子主要是使用 asyncio 包 和 unicodedata 模块,实现通过规范名称查找Unicode 字符。 我们先来看一下代码: # charfinder.py import sys import re import unicodedata import pickle import warnings import itertools import functools from collections import namedtuple RE_WORD = re.compile('\w+') RE_UNICODE_NAME = re.compile('^[A-Z0-9 -]+$') RE_CODEPOINT = re.compile('U\+[0-9A-F]{4, 6}') INDEX_NAME = 'charfinder_index.pickle' MINIMUM_SAVE_LEN = 10000 CJK_UNI_PREFIX = 'CJK UNIFIED IDEOGRAPH' CJK_CMP_PREFIX = 'CJK COMPATIBILITY IDEOGRAPH' sample_chars = [ '$', # DOLLAR SIGN 'A', # LATIN CAPITAL LETTER A 'a', # LATIN SMALL LETTER A '\u20a0', # EURO-CURRENCY SIGN '\u20ac', # EURO SIGN ] CharDescription = namedtuple('CharDescription', 'code_str char name') QueryResult = namedtuple('QueryResult', 'count items') def tokenize(text): ''' :param text: :return: return iterable of uppercased words ''' for match in RE_WORD.finditer(text): yield match.group().upper() def query_type(text): text_upper = text.upper() if 'U+' in text_upper: return 'CODEPOINT' elif RE_UNICODE_NAME.match(text_upper): return 'NAME' else: return 'CHARACTERS' class UnicodeNameIndex: # unicode name 索引类 def __init__(self, chars=None): self.load(chars) def load(self, chars=None): # 加载 unicode name self.index = None if chars is None: try: with open(INDEX_NAME, 'rb') as fp: self.index = pickle.load(fp) except OSError: pass if self.index is None: self.build_index(chars) if len(self.index) > MINIMUM_SAVE_LEN: try: self.save() except OSError as exc: warnings.warn('Could not save {!r}: {}' .format(INDEX_NAME, exc)) def save(self): with open(INDEX_NAME, 'wb') as fp: pickle.dump(self.index, fp) def build_index(self, chars=None): if chars is None: chars = (chr(i) for i in range(32, sys.maxunicode)) index = {} for char in chars: try: name = unicodedata.name(char) except ValueError: continue if name.startswith(CJK_UNI_PREFIX): name = CJK_UNI_PREFIX elif name.startswith(CJK_CMP_PREFIX): name = CJK_CMP_PREFIX for word in tokenize(name): index.setdefault(word, set()).add(char) self.index = index def word_rank(self, top=None): # (len(self.index[key], key) 是一个生成器,需要用list 转成列表,要不然下边排序会报错 res = [list((len(self.index[key], key)) for key in self.index)] res.sort(key=lambda item: (-item[0], item[1])) if top is not None: res = res[:top] return res def word_report(self, top=None): for postings, key in self.word_rank(top): print('{:5} {}'.format(postings, key)) def find_chars(self, query, start=0, stop=None): stop = sys.maxsize if stop is None else stop result_sets = [] for word in tokenize(query): # tokenize 是query 的生成器 a b 会是 ['a', 'b'] 的生成器 chars = self.index.get(word) if chars is None: result_sets = [] break result_sets.append(chars) if not result_sets: return QueryResult(0, ()) result = functools.reduce(set.intersection, result_sets) result = sorted(result) # must sort to support start, stop result_iter = itertools.islice(result, start, stop) return QueryResult(len(result), (char for char in result_iter)) def describe(self, char): code_str = 'U+{:04X}'.format(ord(char)) name = unicodedata.name(char) return CharDescription(code_str, char, name) def find_descriptions(self, query, start=0, stop=None): for char in self.find_chars(query, start, stop).items: yield self.describe(char) def get_descriptions(self, chars): for char in chars: yield self.describe(char) def describe_str(self, char): return '{:7}\t{}\t{}'.format(*self.describe(char)) def find_description_strs(self, query, start=0, stop=None): for char in self.find_chars(query, start, stop).items: yield self.describe_str(char) @staticmethod # not an instance method due to concurrency def status(query, counter): if counter == 0: msg = 'No match' elif counter == 1: msg = '1 match' else: msg = '{} matches'.format(counter) return '{} for {!r}'.format(msg, query) def main(*args): index = UnicodeNameIndex() query = ' '.join(args) n = 0 for n, line in enumerate(index.find_description_strs(query), 1): print(line) print('({})'.format(index.status(query, n))) if __name__ == '__main__': if len(sys.argv) > 1: main(*sys.argv[1:]) else: print('Usage: {} word1 [word2]...'.format(sys.argv[0])) 这个模块读取Python内建的Unicode数据库,为每个字符名称中的每个单词建立索引,然后倒排索引,存入一个字典。 例如,在倒排索引中,‘SUN’ 键对应的条目是一个集合,里面是名称中包含’SUN’ 这个词的10个Unicode字符。倒排索引保存在本地一个名为charfinder_index.pickle 的文件中。如果查询多个单词,会计算从索引中所得集合的交集。 运行示例如下: ...

2017-06-30 · 5 min · 1047 words

python web 框架 Sanci 快速入门

简介 Sanic 是一个和类Flask 的基于Python3.5+的web框架,它编写的代码速度特别快。 除了像Flask 以外,Sanic 还支持以异步请求的方式处理请求。这意味着你可以使用新的 async/await 语法,编写非阻塞的快速的代码。 关于 asyncio 包的介绍,请参考之前的一篇文章 python并发2:使用asyncio处理并发 Github 地址 是 https://github.com/channelcat/sanic,感兴趣的可以去贡献代码。 既然它说速度特别快,我们先看下官方提供的 基准测试结果。 Sanic基准测试 这个测试的程序运行在 AWS 实例上,系统是Ubuntu,只使用了一个进程。 Sanic 的开发者说他们的灵感来自于这篇文章 uvloop: Blazing fast Python networking。 那我们就有必要看下uvloop是个什么库。 uvloop uvloop 是 asyncio 默认事件循环的替代品,实现的功能完整,切即插即用。uvloop是用CPython 写的,建于libuv之上。 uvloop 可以使 asyncio 更快。事实上,它至少比 nodejs、gevent 和其他 Python 异步框架要快两倍 。基于 uvloop 的 asyncio 的速度几乎接近了 Go 程序的速度。 安装 uvloop uvloop 还只能在 *nix 平台 和 Python3.5+以上版本使用。 使用pip安装: pip install uvloop 在 asyncio 代码中使用uvloop 也很简单: import asyncio import uvloop asyncio.set_event_loop_policy(uvloop.EventLoopPolicy()) 这得代码使得对任何asyncio.get_event_loop() 的调用都将返回一个uvloop实例。 ...

2017-06-25 · 4 min · 730 words