分类目录归档:程序相关

C,Python,环境配置等

Form表单的enctype属性和method属性

在WEB开发过程中,Form表单元素是一个使用频率非常高的控件,对于这样一个控件,也许我们并没有认真关注过。今天我们来解读它的enctype属性和method属性。

enctype 属性

enctype属性规定在发送到服务器之前应该如何对表单数据进行编码。它的编码方式有三种:

  • application/x-www-form-urlencoded编码是以name=value键值对为基础,以&连接;
    此为默认值。如果method属性为GET,则编码后的字符串会接到url的后面(其实用其它编码方式,GET的效果也是一样的)。
    如果method属性为POST,则编码后的字符串会被封装到HTTP协议的请求实体中,然后发送到服务器。
  • text/plain编码是以name=value键值对为基础,以\r\n连接;如果服务端的程序是PHP的话,使用此编码,如果method为GET,一切和其它编码一样,如果method为POST,则无论是$_GET、$_POST还是$_REQUEST都无法获取数据,为什么呢?因为PHP对于POST方法处理方法中根本就没有针对这种编码的处理函数。当然,我们可以通过php://input或$HTTP_RAW_POST_DATA获取POST过来的原始值。
  • multipart/form-data编码,这是最为特殊的编码;以其Content-Type后面的boundary为分隔符,将各个控件的值包含的请求实体中。

对于POST请求,一般来说用默认的application/x-www-form-urlencoded就可以了。但是如果有文件控件(type=file)的话,就要用到multipart/form-data了。浏览器会把整个表单以控件为单位分割,并为每个部分加上 Content-Disposition(form-data或者file),Content-Type(默认为text/plain,且没有显示),name(控件的name)等信息,并加上分割符(boundary)。

method 属性

Form的method属性支持POST和GET方法。默认为GET提交。
GET方法用于信息获取,而且应该是安全的和幂等的。所谓安全指该操作用于获取信息而非修改信息。换句话说,GET请求一般不应产生副作用。相当于SQL中的SELECT操作。所谓幂等指对同一URL的多个请求应该返回同样的结果。比如sina网中点击某一个新闻页面,不同的时候返回应该是同一篇文章,如果后台有修改这条新闻,用户所看到的内容不同,但是我们还是会认为这是幂等的。

POST方法表示可能修改变服务器上的资源的请求。这里的修改包括在服务器上增加资源,修改已有资源或者其它修改类型的操作。

虽然method只支持这两个方法,但是HTTP协议还定义了一些其它的方法:
比如PUT方法,它表示完全替换或更新一个已经存在的资源或创建一个新的资源。PUT与POST的差别是这是一个完整的修改,不存在只修改部分。比如DELETE,它表示删除一个资源。

只是,在实际应用中,为了图方便,我们经常使用GET方法实现修改操作,因为这样我们不需要创建表单,如此而已。

关于列表推导式

列表推导式最开始是一些函数式编译语言的句法特征,比如模式匹配,它能极大提高函数式程序的读写能力。最开始并没有列表推导式,只有集合推导式(Set Comprehensions),列表推导式第一次使用是Turner1982年在KRC(Kent Recursive Calculator)上。列表推导式曾经在各种函数式编程语言中出现,如Miranda(一种纯粹的函数式编程语言),Orwell(一种lazy函数式编程语言,对Haskell有较大影响)。

曾经列表推导式被称为集合抽象,由于抽象(abstractions)这个词语的英文单词在若干个地方有用到,其意思太多了,所以引入了推导式(comprehensions)这样一个词语。从数学上的策梅洛-弗兰克尔集合论(Zermelo-Fraenkel Set Theory)(http://en.wikipedia.org/wiki/Zermelo-Frankel_set_theory) 来看,列表推导式和集合推导式类似。比如求集合A中奇数的平方

B = {square x | x ∈ A & odd x}

上面的这个示例,如果A集合是{1,2,3,4},那么B集合为{1,9}

如果我们把这些数学符号换成常见的编程符号,如:

ys = [square x | x <- xs; odd x]

或者我们将<-再变为for in,再加上if语句,是不是就是Python的列表推导式了。

vec = [1, 2, 3, 4]
rs = [x * x for x in vec if x % 2 != 0]
print rs

对应上面的Python示例,我们看下在Python中,列表推导式的一般形式:

[表达式 for item1 in 序列1 ... for itemN in 序列N if 条件表达式]

上面的表达式分为三部分,最左边是生成每个元素的表达式,然后是for 迭代过程,最右边可以设定一个if 判断作为过滤条件。

[]内的列表写以写为一行,也可以写为多行,一般来说多行更易读些,看个人喜好吧。

对于Python而言,列表推导式(List Comprehensions)是其最强有力的语法之一,常用于从集合对象中有选择地获取并计算元素,虽然多数情况下可以使用for、if等语句组合完成同样的任务。

其本质是一种语法糖,它提供了一种简洁高效的方式来创建列表和迭代器, 而不必借助map(), filter(), 或者lambda。
简单的列表推导可以比其它的列表创建方法更加清晰简单. 生成器表达式可以十分高效, 因为它们避免了创建整个列表。这里的优点一般是指使用简单的列表推导式时,而对于复杂的列表推导式虽然可以高效,但是生成的表达式可能难以阅读(不排除通过某些注释或排版达到优化可读性的目的)。列表推导式适用于简单情况. 每个部分应该单独置于一行: 映射表达式, for语句, 过滤器表达式. 禁止多重for语句或过滤器表达式. 复杂情况下还是使用循环吧。

如果我们要用PHP去实现列表推导式,应当如何表现呢?(这里假设我们需要实现这样一个语法糖)

有如下想法,其一般形式如下:

list{表达式1, 表达式2, ... if (条件表达式),  $list1 as $key1 => $row1, $list2 as $key2 => $row2, ...}
 
 
//如下示例:
 
list{echo $key1, echo $row2, if ($key1 > $key2), $a as $key => $row, $b as $key2 => $row2, }

在if语句前可以有多个表达式处理,以逗号隔开;
在if语句后面可以有多个列表,以逗号隔开;

也许这个YY有点纠结,只是对于PHP来说,这个糖果也许没那么重要?

参考资料:

Jones – 《The Implementation of Functional Programming Languages》, PH, 1987

http://zh-google-styleguide.googlecode.com/hg-history/2a227ce093e7b70085818bba22061d9393f3bb99/pyguide/python_language_rules.txt

一次查询优化过程

问题描述

在正在维护的系统中有一个数据分析的模块,用于来分析一些用户访问的数据。其中有一个操作是查找某URL地址对应的ID。在这里ID与URL是存储MySQL数据的一个表中,此表就两个字段,id,url.表存储引擎类型MyISAM,当然URL字段是加了索引的。现在每天按小时定时分析数据,开始程序没有问题,当几年的数据积累下来,特别是最近业务量增加时,明显感觉到程序的执行过程变得很漫长,通过xdebug分析一次执行过程的所有执行时间,发现瓶颈在于前面所提到的通过URL地址对应的ID。下面就开始了漫长的优化之路。

第一次优化:优化细节,针对特殊地址的优化

由于应急,先考虑一些细节的调整:
首先,我们针对一些特殊的地址进行了处理,直接返回ID,比如没有URL的情况,比如Google的首页地址;
然后,我们针对一些常见的地址作为Hash存储,以及一些热数据进行内存缓存。

结果,提高一些性能,能满足当时的业务需求,特别是当特殊的地址较多的时候,其情况还是非常乐观的。

过了一段时间,到了另一个高峰点,发现此分析模块仍然会时不时的出些问题,导致数据不准确,各种情况,各种应付…不得已,开始思考另一个方案。

第一个方案: key-value数据库

依据前面的对于热点数据,将其存储到内存缓存的思路,我们考虑是否将所有数据都作为热点数据,于是先做前期预研,将部分数据导到redis中,发现速度有了极大的提升,大概在500倍的一个量级,那叫一个激动,于是决定将所有数据都迁移到redis中,想法是美好的,结果是不言而喻的。失败了。为什么呢?很简单,没资源,我们没有那么大的内存存储N个上千万的表。

第二个方案:基于单词查找树的文件结构

从前面的key-value数据库方案看,存储url到id的映射,在实现思路上还是可行的,于是开动了小心思,既然内存不够用,那么用硬盘呢?

这个既便宜又实惠,是居家旅行,杀人越货的必备良品。于是计划以URL的主域名为目录,以每个地址的md5值为文件名,每个文件存储这个地址对应的ID。考虑到域名可能很多,于是计划使用变形的单词查找树来设置多级目录,但是当域名地址太长时,文件系统在生成目录的时候会出错,只得去掉单词查找树,使用某种简单的按主域名转化后的字符串转建立目录。在预研时,先生成了5万数据的文件结构,发现生成的速率很慢,并且由于大量小文件的生成,对于整个目录的查询会很慢,但对于单个文件的读取还是很快的,然而将随着数据生成越来越多,当达到40万时,整个目录占用了大概4G+的硬盘空间。

假想一下,当1千万的数据以这种方式存储到硬盘上,会是多少?如果数据持续增加呢?到达一个亿呢?感觉到不靠谱了。于是继续想下一种方案,这个方案继续执行。

第三个方案:信赖于数据库,增加md5值存储的字段

在第二种方案中,我们是使用md5值作为文件名,在想在数据库对于md5后的文件名的查找会更快一些呢?在ID和URL映射表中,增加一个字段url_md5,存储url使用md5后的值,在这里我们直接使用MySQL的md5函数更新表数据,测试后,发现其速度有了显著提升,单个查找操作大概能提升50倍+。整体性能提升5倍的样子。于是,果断采用此方案,结果是该分析模块一直没啥事发生。(^_^)

总结

  1. 不要过早优化
  2. 优化要先找瓶颈
  3. 数据结构和对工具的使用很重要
  4. 优化时需要理清问题的根本原因是什么,最好能到理论层面或实现原理层面。