java图形界面编程基础代码

java图形界面编程基础代码Python 简介 Python 是著名的 龟叔 Guido van Rossum 在 1989 年圣诞节期间 为了打发无聊的圣诞节而编写的一个编程语言 现在 全世界差不多有 600 多种编程语言 但流行的编程语言也就那么 20 来种 如果你听说过 TIOBE 排行榜 你就能知道编程语言的大致流行程度 这是最近 10 年最常用的 10 种编程语言的变化图 总的来说 这几种编程语言各有千秋

大家好,我是讯享网,很高兴认识大家。



Python简介

Python是著名的“龟叔”Guido van Rossum在1989年圣诞节期间,为了打发无聊的圣诞节而编写的一个编程语言。

现在,全世界差不多有600多种编程语言,但流行的编程语言也就那么20来种。如果你听说过TIOBE排行榜,你就能知道编程语言的大致流行程度。这是最近10年最常用的10种编程语言的变化图:

总的来说,这几种编程语言各有千秋。C语言是可以用来编写操作系统的贴近硬件的语言,所以,C语言适合开发那些追求运行速度、充分发挥硬件性能的程序。而Python是用来编写应用程序的高级编程语言。

当你用一种语言开始作真正的软件开发时,你除了编写代码外,还需要很多基本的已经写好的现成的东西,来帮助你加快开发进度。比如说,要编写一个电子邮件客户端,如果先从最底层开始编写网络协议相关的代码,那估计一年半载也开发不出来。高级编程语言通常都会提供一个比较完善的基础代码库,让你能直接调用,比如,针对电子邮件协议的SMTP库,针对桌面环境的GUI库,在这些已有的代码库的基础上开发,一个电子邮件客户端几天就能开发出来。

Python就为我们提供了非常完善的基础代码库,覆盖了网络、文件、GUI、数据库、文本等大量内容,被形象地称作“内置电池(batteries included)”。用Python开发,许多功能不必从零编写,直接使用现成的即可。

除了内置的库外,Python还有大量的第三方库,也就是别人开发的,供你直接使用的东西。当然,如果你开发的代码通过很好的封装,也可以作为第三方库给别人使用。

许多大型网站就是用Python开发的,例如YouTube、Instagram,还有国内的豆瓣。很多大公司,包括Google、Yahoo等,甚至NASA(美国航空航天局)都大量地使用Python。

龟叔给Python的定位是“优雅”、“明确”、“简单”,所以Python程序看上去总是简单易懂,初学者学Python,不但入门容易,而且将来深入下去,可以编写那些非常非常复杂的程序。

总的来说,Python的哲学就是简单优雅,尽量写容易看明白的代码,尽量写少的代码。如果一个资深程序员向你炫耀他写的晦涩难懂、动不动就几万行的代码,你可以尽情地嘲笑他。

那Python适合开发哪些类型的应用呢?

首选是网络应用,包括网站、后台服务等等;

其次是许多日常需要的小工具,包括系统管理员需要的脚本任务等等;

另外就是把其他语言开发的程序再包装起来,方便使用。

最后说说Python的缺点。

任何编程语言都有缺点,Python也不例外。优点说过了,那Python有哪些缺点呢?

第一个缺点就是运行速度慢,和C程序相比非常慢,因为Python是解释型语言,你的代码在执行时会一行一行地翻译成CPU能理解的机器码,这个翻译过程非常耗时,所以很慢。而C程序是运行前直接编译成CPU能执行的机器码,所以非常快。

但是大量的应用程序不需要这么快的运行速度,因为用户根本感觉不出来。例如开发一个下载MP3的网络应用程序,C程序的运行时间需要0.001秒,而Python程序的运行时间需要0.1秒,慢了100倍,但由于网络更慢,需要等待1秒,你想,用户能感觉到1.001秒和1.1秒的区别吗?这就好比F1赛车和普通的出租车在北京三环路上行驶的道理一样,虽然F1赛车理论时速高达400公里,但由于三环路堵车的时速只有20公里,因此,作为乘客,你感觉的时速永远是20公里。

第二个缺点就是代码不能加密。如果要发布你的Python程序,实际上就是发布源代码,这一点跟C语言不同,C语言不用发布源代码,只需要把编译后的机器码(也就是你在Windows上常见的xxx.exe文件)发布出去。要从机器码反推出C代码是不可能的,所以,凡是编译型的语言,都没有这个问题,而解释型的语言,则必须把源码发布出去。

这个缺点仅限于你要编写的软件需要卖给别人挣钱的时候。好消息是目前的互联网时代,靠卖软件授权的商业模式越来越少了,靠网站和移动应用卖服务的模式越来越多了,后一种模式不需要把源码给别人。

再说了,现在如火如荼的开源运动和互联网自由开放的精神是一致的,互联网上有无数非常优秀的像Linux一样的开源代码,我们千万不要高估自己写的代码真的有非常大的“商业价值”。那些大公司的代码不愿意开放的更重要的原因是代码写得太烂了,一旦开源,就没人敢用他们的产品了。

当然,Python还有其他若干小缺点,请自行忽略,就不一一列举了。

java图形界面编程基础代码

安装Python

因为Python是跨平台的,它可以运行在Windows、Mac和各种Linux/Unix系统上。在Windows上写Python程序,放到Linux上也是能够运行的。

要开始学习Python编程,首先就得把Python安装到你的电脑里。安装后,你会得到Python解释器(就是负责运行Python程序的),一个命令行交互环境,还有一个简单的集成开发环境。

安装Python 3.5

目前,Python有两个版本,一个是2.x版,一个是3.x版,这两个版本是不兼容的。由于3.x版越来越普及,我们的教程将以最新的Python 3.5版本为基础。请确保你的电脑上安装的Python版本是最新的3.5.x,这样,你才能无痛学习这个教程。

在Mac上安装Python

如果你正在使用Mac,系统是OS X 10.8~10.10,那么系统自带的Python版本是2.7。要安装最新的Python 3.5,有两个方法:

方法一:从Python官网下载Python 3.5的安装程序(网速慢的同学请移步国内镜像),双击运行并安装;

方法二:如果安装了Homebrew,直接通过命令安装即可。

在Linux上安装Python

如果你正在使用Linux,那我可以假定你有Linux系统管理经验,自行安装Python 3应该没有问题,否则,请换回Windows系统。

对于大量的目前仍在使用Windows的同学,如果短期内没有打算换Mac,就可以继续阅读以下内容。

在Windows上安装Python

首先,根据你的Windows版本(64位还是32位)从Python的官方网站下载Python 3.5对应的64位安装程序或32位安装程序(网速慢的同学请移步国内镜像),然后,运行下载的EXE安装包:

特别要注意勾上,然后点“Install Now”即可完成安装。

默认会安装到目录下,然后打开命令提示符窗口,敲入python后,会出现两种情况:

情况一:

看到上面的画面,就说明Python安装成功!

你看到提示符就表示我们已经在Python交互式环境中了,可以输入任何Python代码,回车后会立刻得到执行结果。现在,输入并回车,就可以退出Python交互式环境(直接关掉命令行窗口也可以)。

情况二:得到一个错误:

这是因为Windows会根据一个的环境变量设定的路径去查找,如果没找到,就会报错。如果在安装时漏掉了勾选,那就要手动把所在的路径添加到Path中。

如果你不知道怎么修改环境变量,建议把Python安装程序重新运行一遍,务必记得勾上。

小结

学会如何把Python安装到计算机中,并且熟练打开和退出Python交互式环境。

在Windows上运行Python时,请先启动命令行,然后运行。

在Mac和Linux上运行Python时,请打开终端,然后运行。

Python解释器

当我们编写Python代码时,我们得到的是一个包含Python代码的以为扩展名的文本文件。要运行代码,就需要Python解释器去执行文件。

由于整个Python语言从规范到解释器都是开源的,所以理论上,只要水平够高,任何人都可以编写Python解释器来执行Python代码(当然难度很大)。事实上,确实存在多种Python解释器。

CPython

当我们从Python官方网站下载并安装好Python 3.5后,我们就直接获得了一个官方版本的解释器:CPython。这个解释器是用C语言开发的,所以叫CPython。在命令行下运行就是启动CPython解释器。

CPython是使用最广的Python解释器。教程的所有代码也都在CPython下执行。

IPython

IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方式上有所增强,但是执行Python代码的功能和CPython是完全一样的。好比很多国产浏览器虽然外观不同,但内核其实都是调用了IE。

CPython用作为提示符,而IPython用作为提示符。

PyPy

PyPy是另一个Python解释器,它的目标是执行速度。PyPy采用JIT技术,对Python代码进行动态编译(注意不是解释),所以可以显著提高Python代码的执行速度。

绝大部分Python代码都可以在PyPy下运行,但是PyPy和CPython有一些是不同的,这就导致相同的Python代码在两种解释器下执行可能会有不同的结果。如果你的代码要放到PyPy下执行,就需要了解PyPy和CPython的不同点。

Jython

Jython是运行在Java平台上的Python解释器,可以直接把Python代码编译成Java字节码执行。

IronPython

IronPython和Jython类似,只不过IronPython是运行在微软.Net平台上的Python解释器,可以直接把Python代码编译成.Net的字节码。

小结

Python的解释器很多,但使用最广泛的还是CPython。如果要和Java或.Net平台交互,最好的办法不是用Jython或IronPython,而是通过网络调用来交互,确保各程序之间的独立性。

本教程的所有代码只确保在CPython 3.5版本下运行。请务必在本地安装CPython(也就是从Python官方网站下载的安装程序)。

第一个Python程序

现在,了解了如何启动和退出Python的交互式环境,我们就可以正式开始编写Python代码了。

在写代码之前,请千万不要用“复制”-“粘贴”把代码从页面粘贴到你自己的电脑上。写程序也讲究一个感觉,你需要一个字母一个字母地把代码自己敲进去,在敲代码的过程中,初学者经常会敲错代码,所以,你需要仔细地检查、对照,才能以最快的速度掌握如何写程序。

在交互式环境的提示符下,直接输入代码,按回车,就可以立刻得到代码执行结果。现在,试试输入,看看计算结果是不是300:

讯享网

很简单吧,任何有效的数学计算都可以算出来。

如果要让Python打印出指定的文字,可以用函数,然后把希望打印的文字用单引号或者双引号括起来,但不能混用单引号和双引号:

 

这种用单引号或者双引号括起来的文本在程序中叫字符串,今后我们还会经常遇到。

最后,用退出Python,我们的第一个Python程序完成!唯一的缺憾是没有保存下来,下次运行时还要再输入一遍代码。

小结

在Python交互式命令行下,可以直接输入代码,然后执行,并立刻得到结果。

使用文本编辑器

在Python的交互式命令行写程序,好处是一下就能得到结果,坏处是没法保存,下次还想运行的时候,还得再敲一遍。

所以,实际开发的时候,我们总是使用一个文本编辑器来写代码,写完了,保存为一个文件,这样,程序就可以反复运行了。

现在,我们就把上次的程序用文本编辑器写出来,保存下来。

那么问题来了:文本编辑器到底哪家强?

推荐两款文本编辑器:

一个是Sublime Text,免费使用,但是不付费会弹出提示框:

一个是Notepad++,免费使用,有中文界面:

请注意,用哪个都行,但是绝对不能用Word和Windows自带的记事本。Word保存的不是纯文本文件,而记事本会自作聪明地在文件开始的地方加上几个特殊字符(UTF-8 BOM),结果会导致程序运行出现莫名其妙的错误。

安装好文本编辑器后,输入以下代码:

讯享网

注意前面不要有任何空格。然后,选择一个目录,例如,把文件保存为,就可以打开命令行窗口,把当前目录切换到所在目录,就可以运行这个程序了:

 

也可以保存为别的名字,比如,但是必须要以结尾,其他的都不行。此外,文件名只能是英文字母、数字和下划线的组合。

如果当前目录下没有这个文件,运行就会报错:

 

报错的意思就是,无法打开这个文件,因为文件不存在。这个时候,就要检查一下当前目录下是否有这个文件了。如果存放在另外一个目录下,要首先用命令切换当前目录:

命令行模式和Python交互模式

请注意区分命令行模式和Python交互模式。

看到类似是在Windows提供的命令行模式:

在命令行模式下,可以执行进入Python交互式环境,也可以执行运行一个文件。

看到是在Python交互式环境下:

在Python交互式环境下,只能输入Python代码并立刻执行。

此外,在命令行模式运行文件和在Python交互式环境下直接运行Python代码有所不同。Python交互式环境会把每一行Python代码的结果自动打印出来,但是,直接运行Python代码却不会。

例如,在Python交互式环境下,输入:

 

直接可以看到结果。

但是,写一个的文件,内容如下:

 

然后在命令行模式下执行:

 

发现什么输出都没有。

这是正常的。想要输出结果,必须自己用打印出来。把改造一下:

 

再执行,就可以看到结果:

 
直接运行py文件

还有同学问,能不能像.exe文件那样直接运行文件呢?在Windows上是不行的,但是,在Mac和Linux上是可以的,方法是在文件的第一行加上一个特殊的注释:

 

然后,通过命令给以执行权限:

 

就可以直接运行了,比如在Mac下运行:

小结

用文本编辑器写Python程序,然后保存为后缀为的文件,就可以用Python直接运行这个程序了。

Python的交互模式和直接运行文件有什么区别呢?

直接输入进入交互模式,相当于启动了Python解释器,但是等待你一行一行地输入源代码,每输入一行就执行一行。

直接运行文件相当于启动了Python解释器,然后一次性把文件的源代码给执行了,你是没有机会以交互的方式输入源代码的。

用Python开发程序,完全可以一边在文本编辑器里写代码,一边开一个交互式命令窗口,在写代码的过程中,把部分代码粘到命令行去验证,事半功倍!前提是得有个27'的超大显示器!

参考源码

hello.py

Python代码运行助手

Python代码运行助手可以让你在线输入Python代码,然后通过本机运行的一个Python脚本来执行代码。原理如下:

  • 在网页输入代码:
  • 点击按钮,代码被发送到本机正在运行的Python代码运行助手;
  • Python代码运行助手将代码保存为临时文件,然后调用Python解释器执行代码;
  • 网页显示代码执行结果:
下载

点击右键,目标另存为:learning.py

备用下载地址:learning.py

运行

在存放的目录下运行命令:

 

如果看到表示运行成功,不要关闭命令行窗口,最小化放到后台运行即可:

试试效果

需要支持HTML5的浏览器:

  • IE >= 9
  • Firefox
  • Chrome
  • Sarafi
 

输入和输出

输出

用在括号中加上字符串,就可以向屏幕上输出指定的文字。比如输出,用代码实现如下:

 

函数也可以接受多个字符串,用逗号“,”隔开,就可以连成一串输出:

 

会依次打印每个字符串,遇到逗号“,”会输出一个空格,因此,输出的字符串是这样拼起来的:

也可以打印整数,或者计算结果:

 

因此,我们可以把计算的结果打印得更漂亮一点:

 

注意,对于,Python解释器自动计算出结果,但是,是字符串而非数学公式,Python把它视为字符串,请自行解释上述打印结果。

输入

现在,你已经可以用输出你想要的结果了。但是,如果要让用户从电脑输入一些字符怎么办?Python提供了一个,可以让用户输入字符串,并存放到一个变量里。比如输入用户的名字:

 

当你输入并按下回车后,Python交互式命令行就在等待你的输入了。这时,你可以输入任意字符,然后按回车后完成输入。

输入完成后,不会有任何提示,Python交互式命令行又回到状态了。那我们刚才输入的内容到哪去了?答案是存放到变量里了。可以直接输入查看变量内容:

 

什么是变量?请回忆初中数学所学的代数基础知识:

设正方形的边长为,则正方形的面积为。把边长看做一个变量,我们就可以根据的值计算正方形的面积,比如:

若a=2,则面积为a x a = 2 x 2 = 4;

若a=3.5,则面积为a x a = 3.5 x 3.5 = 12.25。

在计算机程序中,变量不仅可以为整数或浮点数,还可以是字符串,因此,作为一个变量就是一个字符串。

要打印出变量的内容,除了直接写然后按回车外,还可以用函数:

 

有了输入和输出,我们就可以把上次打印的程序改成有点意义的程序了:

 

运行上面的程序,第一行代码会让用户输入任意字符作为自己的名字,然后存入变量中;第二行代码会根据用户的名字向用户说,比如输入:

 

但是程序运行的时候,没有任何提示信息告诉用户:“嘿,赶紧输入你的名字”,这样显得很不友好。幸好,可以让你显示一个字符串来提示用户,于是我们把代码改成:

 

再次运行这个程序,你会发现,程序一运行,会首先打印出,这样,用户就可以根据提示,输入名字后,得到的输出:

 

每次运行该程序,根据用户输入的不同,输出结果也会不同。

在命令行下,输入和输出就是这么简单。

小结

任何计算机程序都是为了执行一个特定的任务,有了输入,用户才能告诉计算机程序所需的信息,有了输出,程序运行后才能告诉用户任务的结果。

输入是Input,输出是Output,因此,我们把输入输出统称为Input/Output,或者简写为IO。

和是在命令行下面最基本的输入和输出,但是,用户也可以通过其他更高级的图形界面完成输入和输出,比如,在网页上的一个文本框输入自己的名字,点击“确定”后在网页上看到输出信息。

练习

请利用输出:

 
参考源码

do_input.py

Python基础

Python是一种计算机编程语言。计算机编程语言和我们日常使用的自然语言有所不同,最大的区别就是,自然语言在不同的语境下有不同的理解,而计算机要根据编程语言执行任务,就必须保证编程语言写出的程序决不能有歧义,所以,任何一种编程语言都有自己的一套语法,编译器或者解释器就是负责把符合语法的程序代码转换成CPU能够执行的机器码,然后执行。Python也不例外。

Python的语法比较简单,采用缩进方式,写出来的代码就像下面的样子:

 

以开头的语句是注释,注释是给人看的,可以是任意内容,解释器会忽略掉注释。其他每一行都是一个语句,当语句以冒号结尾时,缩进的语句视为代码块。

缩进有利有弊。好处是强迫你写出格式化的代码,但没有规定缩进是几个空格还是Tab。按照约定俗成的管理,应该始终坚持使用4个空格的缩进。

缩进的另一个好处是强迫你写出缩进较少的代码,你会倾向于把一段很长的代码拆分成若干函数,从而得到缩进较少的代码。

缩进的坏处就是“复制-粘贴”功能失效了,这是最坑爹的地方。当你重构代码时,粘贴过去的代码必须重新检查缩进是否正确。此外,IDE很难像格式化Java代码那样格式化Python代码。

最后,请务必注意,Python程序是大小写敏感的,如果写错了大小写,程序会报错。

小结

Python使用缩进来组织代码块,请务必遵守约定俗成的习惯,坚持使用4个空格的缩进。

在文本编辑器中,需要设置把Tab自动转换为4个空格,确保不混用Tab和空格。

数据类型和变量

数据类型

计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各种数值。但是,计算机能处理的远不止数值,还可以处理文本、图形、音频、视频、网页等各种各样的数据,不同的数据,需要定义不同的数据类型。在Python中,能够直接处理的数据类型有以下几种:

整数

Python可以处理任意大小的整数,当然包括负整数,在程序中的表示方法和数学上的写法一模一样,例如:,,,,等等。

计算机由于使用二进制,所以,有时候用十六进制表示整数比较方便,十六进制用前缀和0-9,a-f表示,例如:,,等等。

浮点数

浮点数也就是小数,之所以称为浮点数,是因为按照科学记数法表示时,一个浮点数的小数点位置是可变的,比如,1.23x109和12.3x108是完全相等的。浮点数可以用数学写法,如,,,等等。但是对于很大或很小的浮点数,就必须用科学计数法表示,把10用e替代,1.23x109就是,或者,0.000012可以写成,等等。

整数和浮点数在计算机内部存储的方式是不同的,整数运算永远是精确的(除法难道也是精确的?是的!),而浮点数运算则可能会有四舍五入的误差。

字符串

字符串是以单引号或双引号括起来的任意文本,比如,等等。请注意,或本身只是一种表示方式,不是字符串的一部分,因此,字符串只有,,这3个字符。如果本身也是一个字符,那就可以用括起来,比如包含的字符是,,,空格,,这6个字符。

如果字符串内部既包含又包含怎么办?可以用转义字符来标识,比如:

 

表示的字符串内容是:

 

转义字符可以转义很多字符,比如表示换行,表示制表符,字符本身也要转义,所以表示的字符就是,可以在Python的交互式命令行用打印字符串看看:

 

如果字符串里面有很多字符都需要转义,就需要加很多,为了简化,Python还允许用表示内部的字符串默认不转义,可以自己试试:

 

如果字符串内部有很多换行,用写在一行里不好阅读,为了简化,Python允许用的格式表示多行内容,可以自己试试:

 

上面是在交互式命令行内输入,注意在输入多行内容时,提示符由变为,提示你可以接着上一行输入。如果写成程序,就是:

 

多行字符串还可以在前面加上使用,请自行测试。

布尔值

布尔值和布尔代数的表示完全一致,一个布尔值只有、两种值,要么是,要么是,在Python中,可以直接用、表示布尔值(请注意大小写),也可以通过布尔运算计算出来:

 

布尔值可以用、和运算。

运算是与运算,只有所有都为,运算结果才是:

 

运算是或运算,只要其中有一个为,运算结果就是:

 

运算是非运算,它是一个单目运算符,把变成,变成:

 

布尔值经常用在条件判断中,比如:

 
空值

空值是Python里一个特殊的值,用表示。不能理解为,因为是有意义的,而是一个特殊的空值。

此外,Python还提供了列表、字典等多种数据类型,还允许创建自定义数据类型,我们后面会继续讲到。

变量

变量的概念基本上和初中代数的方程变量是一致的,只是在计算机程序中,变量不仅可以是数字,还可以是任意数据类型。

变量在程序中就是用一个变量名表示了,变量名必须是大小写英文、数字和的组合,且不能用数字开头,比如:

 

变量是一个整数。

 

变量是一个字符串。

 

变量是一个布尔值。

在Python中,等号是赋值语句,可以把任意数据类型赋值给变量,同一个变量可以反复赋值,而且可以是不同类型的变量,例如:

 

这种变量本身类型不固定的语言称之为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值的时候类型不匹配,就会报错。例如Java是静态语言,赋值语句如下(// 表示注释):

 

和静态语言相比,动态语言更灵活,就是这个原因。

请不要把赋值语句的等号等同于数学的等号。比如下面的代码:

 

如果从数学上理解那无论如何是不成立的,在程序中,赋值语句先计算右侧的表达式,得到结果,再赋给变量。由于之前的值是,重新赋值后,的值变成。

最后,理解变量在计算机内存中的表示也非常重要。当我们写:

 

时,Python解释器干了两件事情:

  1. 在内存中创建了一个的字符串;
  2. 在内存中创建了一个名为的变量,并把它指向。

也可以把一个变量赋值给另一个变量,这个操作实际上是把变量指向变量所指向的数据,例如下面的代码:

 

最后一行打印出变量的内容到底是呢还是?如果从数学意义上理解,就会错误地得出和相同,也应该是,但实际上的值是,让我们一行一行地执行代码,就可以看到到底发生了什么事:

执行,解释器创建了字符串和变量,并把指向:

执行,解释器创建了变量,并把指向指向的字符串:

执行,解释器创建了字符串'XYZ',并把的指向改为,但并没有更改:

所以,最后打印变量的结果自然是了。

常量

所谓常量就是不能变的变量,比如常用的数学常数π就是一个常量。在Python中,通常用全部大写的变量名表示常量:

 

但事实上仍然是一个变量,Python根本没有任何机制保证不会被改变,所以,用全部大写的变量名表示常量只是一个习惯上的用法,如果你一定要改变变量的值,也没人能拦住你。

最后解释一下整数的除法为什么也是精确的。在Python中,有两种除法,一种除法是:

 

除法计算结果是浮点数,即使是两个整数恰好整除,结果也是浮点数:

 

还有一种除法是,称为地板除,两个整数的除法仍然是整数:

 

你没有看错,整数的地板除永远是整数,即使除不尽。要做精确的除法,使用就可以。

因为除法只取结果的整数部分,所以Python还提供一个余数运算,可以得到两个整数相除的余数:

 

无论整数做除法还是取余数,结果永远是整数,所以,整数运算结果永远是精确的。

练习

请打印出以下变量的值:

 
小结

Python支持多种数据类型,在计算机内部,可以把任何数据都看成一个“对象”,而变量就是在程序中用来指向这些数据对象的,对变量赋值就是把数据和变量给关联起来。

注意:Python的整数没有大小限制,而某些语言的整数根据其存储长度是有大小限制的,例如Java对32位整数的范围限制在-。

Python的浮点数也没有大小限制,但是超出一定范围就直接表示为(无限大)。

字符串和编码

字符编码

我们已经讲过了,字符串也是一种数据类型,但是,字符串比较特殊的是还有一个编码问题。

因为计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255(二进制=十进制255),如果要表示更大的整数,就必须用更多的字节。比如两个字节可以表示的最大整数是,4个字节可以表示的最大整数是。

由于计算机是美国人发明的,因此,最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为编码,比如大写字母的编码是,小写字母的编码是。

但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了编码,用来把中文编进去。

你可以想得到的是,全世界有上百种语言,日本把日文编到里,韩国把韩文编到里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。

因此,Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。

Unicode标准也在不断发展,但最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。

现在,捋一捋ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是2个字节。

字母用ASCII编码是十进制的,二进制的;

字符用ASCII编码是十进制的,二进制的,注意字符和整数是不同的;

汉字已经超出了ASCII编码的范围,用Unicode编码是十进制的,二进制的。

你可以猜测,如果把ASCII编码的用Unicode编码,只需要在前面补0就可以,因此,的Unicode编码是。

新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。

所以,本着节约的精神,又出现了把Unicode编码转化为“可变长编码”的编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间:

从上面的表格还可以发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。

搞清楚了ASCII、Unicode和UTF-8的关系,我们就可以总结一下现在计算机系统通用的字符编码工作方式:

在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或者需要传输的时候,就转换为UTF-8编码。

用记事本编辑的时候,从文件读取的UTF-8字符被转换为Unicode字符到内存里,编辑完成后,保存的时候再把Unicode转换为UTF-8保存到文件:


浏览网页的时候,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器:


所以你看到很多网页的源码上会有类似的信息,表示该网页正是用的UTF-8编码。

Python的字符串

搞清楚了令人头疼的字符编码问题后,我们再来研究Python的字符串。

在最新的Python 3版本中,字符串是以Unicode编码的,也就是说,Python的字符串支持多语言,例如:

 
 

对于单个字符的编码,Python提供了函数获取字符的整数表示,函数把编码转换为对应的字符:

 
 

如果知道字符的整数编码,还可以用十六进制这么写:

 
 

两种写法完全是等价的。

由于Python的字符串类型是,在内存中以Unicode表示,一个字符对应若干个字节。如果要在网络上传输,或者保存到磁盘上,就需要把变为以字节为单位的。

Python对类型的数据用带前缀的单引号或双引号表示:

 
 

要注意区分和,前者是,后者虽然内容显示得和前者一样,但的每个字符都只占用一个字节。

以Unicode表示的通过方法可以编码为指定的,例如:

 
 

纯英文的可以用编码为,内容是一样的,含有中文的可以用编码为。含有中文的无法用编码,因为中文编码的范围超过了编码的范围,Python会报错。

在中,无法显示为ASCII字符的字节,用显示。

反过来,如果我们从网络或磁盘上读取了字节流,那么读到的数据就是。要把变为,就需要用方法:

 
 

要计算包含多少个字符,可以用函数:

 
 

函数计算的是的字符数,如果换成,函数就计算字节数:

 
 

可见,1个中文字符经过UTF-8编码后通常会占用3个字节,而1个英文字符只占用1个字节。

在操作字符串时,我们经常遇到和的互相转换。为了避免乱码问题,应当始终坚持使用UTF-8编码对和进行转换。

由于Python源代码也是一个文本文件,所以,当你的源代码中包含中文的时候,在保存源代码时,就需要务必指定保存为UTF-8编码。当Python解释器读取源代码时,为了让它按UTF-8编码读取,我们通常在文件开头写上这两行:

 
 

第一行注释是为了告诉Linux/OS X系统,这是一个Python可执行程序,Windows系统会忽略这个注释;

第二行注释是为了告诉Python解释器,按照UTF-8编码读取源代码,否则,你在源代码中写的中文输出可能会有乱码。

申明了UTF-8编码并不意味着你的文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码:


如果文件本身使用UTF-8编码,并且也申明了,打开命令提示符测试就可以正常显示中文:


格式化

最后一个常见的问题是如何输出格式化的字符串。我们经常会输出类似之类的字符串,而xxx的内容都是根据变量变化的,所以,需要一种简便的格式化字符串的方式。


在Python中,采用的格式化方式和C语言是一致的,用实现,举例如下:

 
 

你可能猜到了,运算符就是用来格式化字符串的。在字符串内部,表示用字符串替换,表示用整数替换,有几个占位符,后面就跟几个变量或者值,顺序要对应好。如果只有一个,括号可以省略。

常见的占位符有:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

br>

br>

 

 

 

 

 

 

 

 

 

 

 

  • 低于18.5:过轻

  • 18.5-25:正常

  • 25-28:过重

  • 28-32:肥胖

  • 高于32:严重肥胖

  •  

    br>

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 查找和插入的速度极快,不会随着key的增加而增加;

  • 需要占用大量的内存,内存浪费多。

  • 查找和插入的时间随着元素的增加而增加;

  • 占用空间小,浪费内存很少。

  • 不可变对象

     

     

     

     

     

     

     

     

     

    br>

    br>

    sup>2

    /sup>

     

    sup>2

    /sup>+1)

     

     

     

     

     

     

     

     

    br>

    br>

    br>

    br>

     

     

     

     

     

     

     

     

     

    sup>2

    /sup> + bx + c = 0

     

     

    sup>2

    /sup>的函数:

     

     

    sup>3

    /sup>怎么办?可以再定义一个函数,但是如果要计算x

    sup>4

    /sup>、x

    sup>5

    /sup>……怎么办?我们不可能定义无限多个函数。

    sup>n

    /sup>,说干就干:

     

     

     

    sup>2

    /sup>,所以,完全可以把第二个参数n的默认值设定为2:

     

     

     

     

     

     

     

     

     

     

     

     

     

    sup>2

    /sup> + b

    sup>2

    /sup> + c

    sup>2

    /sup> + ……。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    尾递归

    优化,事实上尾递归和循环的效果是一样的,所以,把循环看成是一种特殊的尾递归函数也是可以的。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    sup>2

    /sup>,要把这个函数作用在一个list 上,就可以用实现如下:

    br>

     

     

    sup>2

    /sup>,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:

     

     

     

     

     

     

     

     

     

     

     

     

    del>4

    /del>, 5,

    del>6

    /del>, 7,

    del>8

    /del>, 9,

    del>10

    /del>, 11,

    del>12

    /del>, 13,

    del>14

    /del>, 15,

    del>16

    /del>, 17,

    del>18

    /del>, 19,

    del>20

    /del>, ...

    del>6

    /del>, 7,

    del>8

    /del>,

    del>9

    /del>,

    del>10

    /del>, 11,

    del>12

    /del>, 13,

    del>14

    /del>,

    del>15

    /del>,

    del>16

    /del>, 17,

    del>18

    /del>, 19,

    del>20

    /del>, ...

    del>8

    /del>,

    del>9

    /del>,

    del>10

    /del>, 11,

    del>12

    /del>, 13,

    del>14

    /del>,

    del>15

    /del>,

    del>16

    /del>, 17,

    del>18

    /del>, 19,

    del>20

    /del>, ...

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    sup>2

    /sup>时,除了定义一个的函数外,还可以直接传入匿名函数:

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    br>

    br>

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    br>

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    只读

    属性,因为可以根据和当前时间计算出来。

     

  • Dog - 狗狗;

  • Bat - 蝙蝠;

  • Parrot - 鹦鹉;

  • Ostrich - 鸵鸟。

  • br>

    br>

  • 哺乳类:能跑的哺乳类,能飞的哺乳类;

  • 鸟类:能跑的鸟类,能飞的鸟类。

  • br>

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • http://api.server/user/friends

  • http://api.server/user/timeline/list

  •  

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • class的名称;

  • 继承的父类集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;

  • class的方法名称与函数绑定,这里我们把函数绑定到方法名上。

  •  

     

  • 当前准备创建的类的对象;

  • 类的名字;

  • 类继承的父类集合;

  • 类的方法集合。

  •  

     

     

     

     

     

     

  • 排除掉对类的修改;

  • 在当前类(比如)中查找定义的类的所有属性,如果找到一个Field属性,就把它保存到一个的dict中,同时从类属性中删除该Field属性,否则,容易造成运行时错误(实例的属性会遮盖类的同名属性);

  • 把表名保存到中,这里简化为表名默认为类名。

  •  

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 输入正数,比如、、,期待返回值与输入相同;

  • 输入负数,比如、、,期待返回值与输入相反;

  • 输入,期待返回;

  • 输入非数值类型,比如、、,期待抛出。

  •  

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

  • 利用模块编写一个能实现输出的程序。

  • 编写一个程序,能在当前目录以及当前目录的所有子目录下查找文件名包含指定字符串的文件,并打印出相对路径。

  •  

     

     

     

    col>

    col>

    你可以用一个文本文件保存,一行保存一个学生,用隔开:

     
    

    你还可以用JSON格式保存,也是文本文件:

     
    

    你还可以定义各种保存格式,但是问题来了:

    存储和读取需要自己实现,JSON还是标准,自己定义的格式就各式各样了;

    不能做快速查询,只有把数据全部读到内存中才能自己遍历,但有时候数据的大小远远超过了内存(比如蓝光电影,40GB的数据),根本无法全部读入内存。

    为了便于程序保存和读取数据,而且,能直接通过条件快速查询到指定的数据,就出现了数据库(Database)这种专门用于集中存储和查询的软件。

    数据库软件诞生的历史非常久远,早在1950年数据库就诞生了。经历了网状数据库,层次数据库,我们现在广泛使用的关系数据库是20世纪70年代基于关系模型的基础上诞生的。

    关系模型有一套复杂的数学理论,但是从概念上是十分容易理解的。举个学校的例子:

    假设某个XX省YY市ZZ县第一实验小学有3个年级,要表示出这3个年级,可以在Excel中用一个表格画出来:

    每个年级又有若干个班级,要把所有班级表示出来,可以在Excel中再画一个表格:

    这两个表格有个映射关系,就是根据Grade_ID可以在班级表中查找到对应的所有班级:

    也就是Grade表的每一行对应Class表的多行,在关系数据库中,这种基于表(Table)的一对多的关系就是关系数据库的基础。

    根据某个年级的ID就可以查找所有班级的行,这种查询语句在关系数据库中称为SQL语句,可以写成:

     
    

    结果也是一个表:

     
    

    类似的,Class表的一行记录又可以关联到Student表的多行记录:

    由于本教程不涉及到关系数据库的详细内容,如果你想从零学习关系数据库和基本的SQL语句,推荐Coursera课程:

    英文:https://www.coursera.org/course/db

    中文:http://c.open.163.com/coursera/courseIntro.htm?cid=12

    NoSQL

    你也许还听说过NoSQL数据库,很多NoSQL宣传其速度和规模远远超过关系数据库,所以很多同学觉得有了NoSQL是否就不需要SQL了呢?千万不要被他们忽悠了,连SQL都不明白怎么可能搞明白NoSQL呢?

    数据库类别

    既然我们要使用关系数据库,就必须选择一个关系数据库。目前广泛使用的关系数据库也就这么几种:

    付费的商用数据库:

    • Oracle,典型的高富帅;
    • SQL Server,微软自家产品,Windows定制专款;
    • DB2,IBM的产品,听起来挺高端;
    • Sybase,曾经跟微软是好基友,后来关系破裂,现在家境惨淡。

    这些数据库都是不开源而且付费的,最大的好处是花了钱出了问题可以找厂家解决,不过在Web的世界里,常常需要部署成千上万的数据库服务器,当然不能把大把大把的银子扔给厂家,所以,无论是Google、Facebook,还是国内的BAT,无一例外都选择了免费的开源数据库:

    • MySQL,大家都在用,一般错不了;
    • PostgreSQL,学术气息有点重,其实挺不错,但知名度没有MySQL高;
    • sqlite,嵌入式数据库,适合桌面和移动应用。

    作为Python开发工程师,选择哪个免费数据库呢?当然是MySQL。因为MySQL普及率最高,出了错,可以很容易找到解决方法。而且,围绕MySQL有一大堆监控和运维的工具,安装和使用很方便。

    为了能继续后面的学习,你需要从MySQL官方网站下载并安装MySQL Community Server 5.6,这个版本是免费的,其他高级版本是要收钱的(请放心,收钱的功能我们用不上)。

    使用SQLite

    SQLite是一种嵌入式数据库,它的数据库就是一个文件。由于SQLite本身是C写的,而且体积很小,所以,经常被集成到各种应用程序中,甚至在iOS和Android的App中都可以集成。

    Python就内置了SQLite3,所以,在Python中使用SQLite,不需要安装任何东西,直接使用。

    在使用SQLite前,我们先要搞清楚几个概念:

    表是数据库中存放关系数据的集合,一个数据库里面通常都包含多个表,比如学生的表,班级的表,学校的表,等等。表和表之间通过外键关联。

    要操作关系数据库,首先需要连接到数据库,一个数据库连接称为Connection;

    连接到数据库后,需要打开游标,称之为Cursor,通过Cursor执行SQL语句,然后,获得执行结果。

    Python定义了一套操作数据库的API接口,任何数据库要连接到Python,只需要提供符合Python标准的数据库驱动即可。

    由于SQLite的驱动内置在Python标准库中,所以我们可以直接来操作SQLite数据库。

    我们在Python交互式命令行实践一下:

     
    

    我们再试试查询记录:

     
    

    使用Python的DB-API时,只要搞清楚和对象,打开后一定记得关闭,就可以放心地使用。

    使用对象执行,,语句时,执行结果由返回影响的行数,就可以拿到执行结果。

    使用对象执行语句时,通过可以拿到结果集。结果集是一个list,每个元素都是一个tuple,对应一行记录。

    如果SQL语句带有参数,那么需要把参数按照位置传递给方法,有几个占位符就必须对应几个参数,例如:

     
    

    SQLite支持常见的标准SQL语句以及几种常见的数据类型。具体文档请参阅SQLite官方网站。

    小结

    在Python中操作数据库时,要先导入数据库对应的驱动,然后,通过对象和对象操作数据。

    要确保打开的对象和对象都正确地被关闭,否则,资源就会泄露。

    如何才能确保出错的情况下也关闭掉对象和对象呢?请回忆的用法。

    练习

    请编写函数,在Sqlite中根据分数段查找指定的名字:

     
    
    参考源码

    do_sqlite.py

    使用MySQL

    MySQL是Web世界中使用最广泛的数据库服务器。SQLite的特点是轻量级、可嵌入,但不能承受高并发访问,适合桌面和移动应用。而MySQL是为服务器端设计的数据库,能承受高并发访问,同时占用的内存也远远大于SQLite。

    此外,MySQL内部有多种数据库引擎,最常用的引擎是支持数据库事务的InnoDB。

    安装MySQL

    可以直接从MySQL官方网站下载最新的Community Server 5.6.x版本。MySQL是跨平台的,选择对应的平台下载安装文件,安装即可。

    安装时,MySQL会提示输入用户的口令,请务必记清楚。如果怕记不住,就把口令设置为。

    在Windows上,安装时请选择编码,以便正确地处理中文。

    在Mac或Linux上,需要编辑MySQL的配置文件,把数据库默认的编码全部改为UTF-8。MySQL的配置文件默认存放在或者:

     
    

    重启MySQL后,可以通过MySQL的客户端命令行检查编码:

     
    

    看到字样就表示编码设置正确。

    安装MySQL驱动

    由于MySQL服务器以独立的进程运行,并通过网络对外服务,所以,需要支持Python的MySQL驱动来连接到MySQL服务器。MySQL官方提供了mysql-connector-python驱动,但是安装的时候需要给pip命令加上参数:

     
    

    我们演示如何连接到MySQL服务器的test数据库:

     
    

    由于Python的DB-API定义都是通用的,所以,操作MySQL的数据库代码和SQLite类似。

    小结
    • 执行INSERT等操作后要调用提交事务;
    • MySQL的SQL占位符是。
    参考源码

    do_mysql.py

    使用SQLAlchemy

    数据库表是一个二维表,包含多行多列。把一个表的内容用Python的数据结构表示出来的话,可以用一个list表示多行,list的每一个元素是tuple,表示一行记录,比如,包含和的表:

     
    

    Python的DB-API返回的数据结构就是像上面这样表示的。

    但是用tuple表示一行很难看出表的结构。如果把一个tuple用class实例来表示,就可以更容易地看出表的结构来:

     
    

    这就是传说中的ORM技术:Object-Relational Mapping,把关系数据库的表结构映射到对象上。是不是很简单?

    但是由谁来做这个转换呢?所以ORM框架应运而生。

    在Python中,最有名的ORM框架是SQLAlchemy。我们来看看SQLAlchemy的用法。

    首先通过pip安装SQLAlchemy:

     
    

    然后,利用上次我们在MySQL的test数据库中创建的表,用SQLAlchemy来试试:

    第一步,导入SQLAlchemy,并初始化DBSession:

     
    

    以上代码完成SQLAlchemy的初始化和具体每个表的class定义。如果有多个表,就继续定义其他class,例如School:

     
    

    用来初始化数据库连接。SQLAlchemy用一个字符串表示连接信息:

     
    

    你只需要根据需要替换掉用户名、口令等信息即可。

    下面,我们看看如何向数据库表中添加一行记录。

    由于有了ORM,我们向数据库表中添加一行记录,可以视为添加一个对象:

     
    

    可见,关键是获取session,然后把对象添加到session,最后提交并关闭。对象可视为当前数据库连接。

    如何从数据库表中查询数据呢?有了ORM,查询出来的可以不再是tuple,而是对象。SQLAlchemy提供的查询接口如下:

     
    

    运行结果如下:

     
    

    可见,ORM就是把数据库表的行与相应的对象建立关联,互相转换。

    由于关系数据库的多个表还可以用外键实现一对多、多对多等关联,相应地,ORM框架也可以提供两个对象之间的一对多、多对多等功能。

    例如,如果一个User拥有多个Book,就可以定义一对多关系如下:

     
    

    当我们查询一个User对象时,该对象的books属性将返回一个包含若干个Book对象的list。

    小结

    ORM框架的作用就是把数据库表的一行记录与一个对象互相做自动转换。

    正确使用ORM的前提是了解关系数据库的原理。

    参考源码

    do_sqlalchemy.py

    Web开发

    最早的软件都是运行在大型机上的,软件使用者通过“哑终端”登陆到大型机上去运行软件。后来随着PC机的兴起,软件开始主要运行在桌面上,而数据库这样的软件运行在服务器端,这种Client/Server模式简称CS架构。

    随着互联网的兴起,人们发现,CS架构不适合Web,最大的原因是Web应用程序的修改和升级非常迅速,而CS架构需要每个客户端逐个升级桌面App,因此,Browser/Server模式开始流行,简称BS架构。

    在BS架构下,客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端。浏览器只需要请求服务器,获取Web页面,并把Web页面展示给用户即可。

    当然,Web页面也具有极强的交互性。由于Web页面是用HTML编写的,而HTML具备超强的表现力,并且,服务器端升级后,客户端无需任何部署就可以使用到新的版本,因此,BS架构迅速流行起来。

    今天,除了重量级的软件如Office,Photoshop等,大部分软件都以Web形式提供。比如,新浪提供的新闻、博客、微博等服务,均是Web应用。

    Web应用开发可以说是目前软件开发中最重要的部分。Web开发也经历了好几个阶段:

    1. 静态Web页面:由文本编辑器直接编辑并生成静态的HTML页面,如果要修改Web页面的内容,就需要再次编辑HTML源文件,早期的互联网Web页面就是静态的;
    2. CGI:由于静态Web页面无法与用户交互,比如用户填写了一个注册表单,静态Web页面就无法处理。要处理用户发送的动态数据,出现了Common Gateway Interface,简称CGI,用C/C++编写。
    3. ASP/JSP/PHP:由于Web应用特点是修改频繁,用C/C++这样的低级语言非常不适合Web开发,而脚本语言由于开发效率高,与HTML结合紧密,因此,迅速取代了CGI模式。ASP是微软推出的用VBScript脚本编程的Web开发技术,而JSP用Java来编写脚本,PHP本身则是开源的脚本语言。
    4. MVC:为了解决直接用脚本语言嵌入HTML导致的可维护性差的问题,Web应用也引入了Model-View-Controller的模式,来简化Web开发。ASP发展为ASP.Net,JSP和PHP也有一大堆MVC框架。

    目前,Web开发技术仍在快速发展中,异步开发、新的MVVM前端技术层出不穷。

    Python的诞生历史比Web还要早,由于Python是一种解释型的脚本语言,开发效率高,所以非常适合用来做Web开发。

    Python有上百种Web开发框架,有很多成熟的模板技术,选择Python开发Web应用,不但开发效率高,而且运行速度快。

    本章我们会详细讨论Python Web开发技术。

    HTTP协议简介

    在Web应用中,服务器把网页传给浏览器,实际上就是把网页的HTML代码发送给浏览器,让浏览器显示出来。而浏览器和服务器之间的传输协议是HTTP,所以:

    • HTML是一种用来定义网页的文本,会HTML,就可以编写网页;
    • HTTP是在网络上传输HTML的协议,用于浏览器和服务器的通信。

    在举例子之前,我们需要安装Google的Chrome浏览器。

    为什么要使用Chrome浏览器而不是IE呢?因为IE实在是太慢了,并且,IE对于开发和调试Web应用程序完全是一点用也没有。

    我们需要在浏览器很方便地调试我们的Web应用,而Chrome提供了一套完整地调试工具,非常适合Web开发。

    安装好Chrome浏览器后,打开Chrome,在菜单中选择“视图”,“开发者”,“开发者工具”,就可以显示开发者工具:

    显示网页的结构,显示浏览器和服务器的通信。我们点,确保第一个小红灯亮着,Chrome就会记录所有浏览器和服务器之间的通信:

    当我们在地址栏输入时,浏览器将显示新浪的首页。在这个过程中,浏览器都干了哪些事情呢?通过的记录,我们就可以知道。在中,定位到第一条记录,点击,右侧将显示,点击右侧的,我们就可以看到浏览器发给新浪服务器的请求:

    最主要的头两行分析如下,第一行:

     
    

    表示一个读取请求,将从服务器获得网页数据,表示URL的路径,URL总是以开头,就表示首页,最后的指示采用的HTTP协议版本是1.1。目前HTTP协议的版本就是1.1,但是大部分服务器也支持1.0版本,主要区别在于1.1版本允许多个HTTP请求复用一个TCP连接,以加快传输速度。

    从第二行开始,每一行都类似于:

     
    

    表示请求的域名是。如果一台服务器有多个网站,服务器就需要通过来区分浏览器请求的是哪个网站。

    继续往下找到,点击,显示服务器返回的原始响应数据:

    HTTP响应分为Header和Body两部分(Body是可选项),我们在中看到的Header最重要的几行如下:

     
    

    表示一个成功的响应,后面的是说明。失败的响应有:网页不存在,:服务器内部出错,等等。

     
    

    指示响应的内容,这里是表示HTML网页。请注意,浏览器就是依靠来判断响应的内容是网页还是图片,是视频还是音乐。浏览器并不靠URL来判断响应的内容,所以,即使URL是,它也不一定就是图片。

    HTTP响应的Body就是HTML源码,我们在菜单栏选择“视图”,“开发者”,“查看网页源码”就可以在浏览器中直接查看HTML源码:

    当浏览器读取到新浪首页的HTML源码后,它会解析HTML,显示页面,然后,根据HTML里面的各种链接,再发送HTTP请求给新浪服务器,拿到相应的图片、视频、Flash、JavaScript脚本、CSS等各种资源,最终显示出一个完整的页面。所以我们在下面能看到很多额外的HTTP请求。

    HTTP请求

    跟踪了新浪的首页,我们来总结一下HTTP请求的流程:

    步骤1:浏览器首先向服务器发送HTTP请求,请求包括:

    方法:GET还是POST,GET仅请求资源,POST会附带用户数据;

    路径:/full/url/path;

    域名:由Host头指定:Host: www.sina.com.cn

    以及其他相关的Header;

    如果是POST,那么请求还包括一个Body,包含用户数据。

    步骤2:服务器向浏览器返回HTTP响应,响应包括:

    响应代码:200表示成功,3xx表示重定向,4xx表示客户端发送的请求有错误,5xx表示服务器端处理时发生了错误;

    响应类型:由Content-Type指定;

    以及其他相关的Header;

    通常服务器的HTTP响应会携带内容,也就是有一个Body,包含响应的内容,网页的HTML源码就在Body中。

    步骤3:如果浏览器还需要继续向服务器请求其他资源,比如图片,就再次发出HTTP请求,重复步骤1、2。

    Web采用的HTTP协议采用了非常简单的请求-响应模式,从而大大简化了开发。当我们编写一个页面时,我们只需要在HTTP请求中把HTML发送出去,不需要考虑如何附带图片、视频等,浏览器如果需要请求图片和视频,它会发送另一个HTTP请求,因此,一个HTTP请求只处理一个资源。

    HTTP协议同时具备极强的扩展性,虽然浏览器请求的是的首页,但是新浪在HTML中可以链入其他服务器的资源,比如,从而将请求压力分散到各个服务器上,并且,一个站点可以链接到其他站点,无数个站点互相链接起来,就形成了World Wide Web,简称WWW。

    HTTP格式

    每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。

    HTTP协议是一种文本协议,所以,它的格式也非常简单。HTTP GET请求的格式:

     
    

    每个Header一行一个,换行符是。

    HTTP POST请求的格式:

     
    

    当遇到连续两个时,Header部分结束,后面的数据全部是Body。

    HTTP响应的格式:

     
    

    HTTP响应如果包含body,也是通过来分隔的。请再次注意,Body的数据类型由头来确定,如果是网页,Body就是文本,如果是图片,Body就是图片的二进制数据。

    当存在时,Body数据是被压缩的,最常见的压缩方式是gzip,所以,看到时,需要将Body数据先解压缩,才能得到真正的数据。压缩的目的在于减少Body的大小,加快网络传输。

    要详细了解HTTP协议,推荐“HTTP: The Definitive Guide”一书,非常不错,有中文译本:

    HTTP权威指南

    HTML简介

    网页就是HTML?这么理解大概没错。因为网页中不但包含文字,还有图片、视频、Flash小游戏,有复杂的排版、动画效果,所以,HTML定义了一套语法规则,来告诉浏览器如何把一个丰富多彩的页面显示出来。

    HTML长什么样?上次我们看了新浪首页的HTML源码,如果仔细数数,竟然有6000多行!

    所以,学HTML,就不要指望从新浪入手了。我们来看看最简单的HTML长什么样:

     
    

    可以用文本编辑器编写HTML,然后保存为,双击或者把文件拖到浏览器中,就可以看到效果:

    HTML文档就是一系列的Tag组成,最外层的Tag是。规范的HTML也包含和(注意不要和HTTP的Header、Body搞混了),由于HTML是富文档模型,所以,还有一系列的Tag用来表示链接、图片、表格、表单等等。

    CSS简介

    CSS是Cascading Style Sheets(层叠样式表)的简称,CSS用来控制HTML里的所有元素如何展现,比如,给标题元素加一个样式,变成48号字体,灰色,带阴影:

     
    

    效果如下:

    JavaScript简介

    JavaScript虽然名称有个Java,但它和Java真的一点关系没有。JavaScript是为了让HTML具有交互性而作为脚本语言添加的,JavaScript既可以内嵌到HTML中,也可以从外部链接到HTML中。如果我们希望当用户点击标题时把标题变成红色,就必须通过JavaScript来实现:

     
    

    点击标题后效果如下:

    小结

    如果要学习Web开发,首先要对HTML、CSS和JavaScript作一定的了解。HTML定义了页面的内容,CSS来控制页面元素的样式,而JavaScript负责页面的交互逻辑。

    讲解HTML、CSS和JavaScript就可以写3本书,对于优秀的Web开发人员来说,精通HTML、CSS和JavaScript是必须的,这里推荐一个在线学习网站w3schools:

    http://www.w3schools.com/

    以及一个对应的中文版本:

    http://www.w3school.com.cn/

    当我们用Python或者其他语言开发Web应用时,我们就是要在服务器端动态创建出HTML,这样,浏览器就会向不同的用户显示出不同的Web页面。

    WSGI接口

    了解了HTTP协议和HTML文档,我们其实就明白了一个Web应用的本质就是:

    1. 浏览器发送一个HTTP请求;
    2. 服务器收到请求,生成一个HTML文档;
    3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器;
    4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示。

    所以,最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。Apache、Nginx、Lighttpd等这些常见的静态服务器就是干这件事情的。

    如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

    正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口,让我们专心用Python编写Web业务。

    这个接口就是WSGI:Web Server Gateway Interface。

    WSGI接口定义非常简单,它只要求Web开发者实现一个函数,就可以响应HTTP请求。我们来看一个最简单的Web版本的“Hello, web!”:

     
    

    上面的函数就是符合WSGI标准的一个HTTP处理函数,它接收两个参数:

    • environ:一个包含所有HTTP请求信息的对象;
    • start_response:一个发送HTTP响应的函数。

    在函数中,调用:

     
    

    就发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次函数。函数接收两个参数,一个是HTTP响应码,一个是一组表示的HTTP Header,每个Header用一个包含两个的表示。

    通常情况下,都应该把头发送给浏览器。其他很多常用的HTTP Header也应该发送。

    然后,函数的返回值将作为HTTP响应的Body发送给浏览器。

    有了WSGI,我们关心的就是如何从这个对象拿到HTTP请求信息,然后构造HTML,通过发送Header,最后返回Body。

    整个函数本身没有涉及到任何解析HTTP的部分,也就是说,底层代码不需要我们自己编写,我们只负责在更高层次上考虑如何响应请求就可以了。

    不过,等等,这个函数怎么调用?如果我们自己调用,两个参数和我们没法提供,返回的也没法发给浏览器。

    所以函数必须由WSGI服务器来调用。有很多符合WSGI规范的服务器,我们可以挑选一个来用。但是现在,我们只想尽快测试一下我们编写的函数真的可以把HTML输出到浏览器,所以,要赶紧找一个最简单的WSGI服务器,把我们的Web应用程序跑起来。

    好消息是Python内置了一个WSGI服务器,这个模块叫wsgiref,它是用纯Python编写的WSGI服务器的参考实现。所谓“参考实现”是指该实现完全符合WSGI标准,但是不考虑任何运行效率,仅供开发和测试使用。

    运行WSGI服务

    我们先编写,实现Web应用程序的WSGI处理函数:

     
    

    然后,再编写一个,负责启动WSGI服务器,加载函数:

     
    

    确保以上两个文件在同一个目录下,然后在命令行输入来启动WSGI服务器:

    注意:如果端口已被其他程序占用,启动将失败,请修改成其他端口。

    启动成功后,打开浏览器,输入,就可以看到结果了:

    在命令行可以看到wsgiref打印的log信息:

    按终止服务器。

    如果你觉得这个Web应用太简单了,可以稍微改造一下,从里读取,这样可以显示更加动态的内容:

     
    

    你可以在地址栏输入用户名作为URL的一部分,将返回:

    是不是有点Web App的感觉了?

    小结

    无论多么复杂的Web应用程序,入口都是一个WSGI处理函数。HTTP请求的所有输入信息都可以通过获得,HTTP响应的输出都可以通过加上函数返回值作为Body。

    复杂的Web应用程序,光靠一个WSGI函数来处理还是太底层了,我们需要在WSGI之上再抽象出Web框架,进一步简化Web开发。

    参考源码

    hello.py

    do_wsgi.py

    使用Web框架

    了解了WSGI框架,我们发现:其实一个Web App,就是写一个WSGI的处理函数,针对每个HTTP请求进行响应。

    但是如何处理HTTP请求不是问题,问题是如何处理100个不同的URL。

    每一个URL可以对应GET和POST请求,当然还有PUT、DELETE等请求,但是我们通常只考虑最常见的GET和POST请求。

    一个最简单的想法是从变量里取出HTTP请求的信息,然后逐个判断:

     
    

    只是这么写下去代码是肯定没法维护了。

    代码这么写没法维护的原因是因为WSGI提供的接口虽然比HTTP接口高级了不少,但和Web App的处理逻辑比,还是比较低级,我们需要在WSGI接口之上能进一步抽象,让我们专注于用一个函数处理一个URL,至于URL到函数的映射,就交给Web框架来做。

    由于用Python开发一个Web框架十分容易,所以Python有上百个开源的Web框架。这里我们先不讨论各种Web框架的优缺点,直接选择一个比较流行的Web框架——Flask来使用。

    用Flask编写Web App比WSGI接口简单(这不是废话么,要是比WSGI还复杂,用框架干嘛?),我们先用安装Flask:

     
    

    然后写一个,处理3个URL,分别是:

    • :首页,返回;
    • :登录页,显示登录表单;
    • :处理登录表单,显示登录结果。

    注意噢,同一个URL分别有GET和POST两种请求,映射到两个处理函数中。

    Flask通过Python的装饰器在内部自动地把URL和函数给关联起来,所以,我们写出来的代码就像这样:

     
    

    运行,Flask自带的Server在端口上监听:

     
    

    打开浏览器,输入首页地址:

    首页显示正确!

    再在浏览器地址栏输入,会显示登录表单:

    输入预设的用户名和口令,登录成功:

    输入其他错误的用户名和口令,登录失败:

    实际的Web App应该拿到用户名和口令后,去数据库查询再比对,来判断用户是否能登录成功。

    除了Flask,常见的Python Web框架还有:

    • Django:全能型Web框架;
    • web.py:一个小巧的Web框架;
    • Bottle:和Flask类似的Web框架;
    • Tornado:Facebook的开源异步Web框架。

    当然了,因为开发Python的Web框架也不是什么难事,我们后面也会讲到开发Web框架的内容。

    小结

    有了Web框架,我们在编写Web应用时,注意力就从WSGI处理函数转移到URL+对应的处理函数,这样,编写Web App就更加简单了。

    在编写URL处理函数时,除了配置URL外,从HTTP请求拿到用户数据也是非常重要的。Web框架都提供了自己的API来实现这些功能。Flask通过来获取表单的内容。

    参考源码

    do_flask.py

    使用模板

    Web框架把我们从WSGI中拯救出来了。现在,我们只需要不断地编写函数,带上URL,就可以继续Web App的开发了。

    但是,Web App不仅仅是处理逻辑,展示给用户的页面也非常重要。在函数中返回一个包含HTML的字符串,简单的页面还可以,但是,想想新浪首页的6000多行的HTML,你确信能在Python的字符串中正确地写出来么?反正我是做不到。

    俗话说得好,不懂前端的Python工程师不是好的产品经理。有Web开发经验的同学都明白,Web App最复杂的部分就在HTML页面。HTML不仅要正确,还要通过CSS美化,再加上复杂的JavaScript脚本来实现各种交互和动画效果。总之,生成HTML页面的难度很大。

    由于在Python代码里拼字符串是不现实的,所以,模板技术出现了。

    使用模板,我们需要预先准备一个HTML文档,这个HTML文档不是普通的HTML,而是嵌入了一些变量和指令,然后,根据我们传入的数据,替换后,得到最终的HTML,发送给用户:

    这就是传说中的MVC:Model-View-Controller,中文名“模型-视图-控制器”。

    Python处理URL的函数就是C:Controller,Controller负责业务逻辑,比如检查用户名是否存在,取出用户信息等等;

    包含变量的模板就是V:View,View负责显示逻辑,通过简单地替换一些变量,View最终输出的就是用户看到的HTML。

    MVC中的Model在哪?Model是用来传给View的,这样View在替换变量的时候,就可以从Model中取出相应的数据。

    上面的例子中,Model就是一个:

     
    

    只是因为Python支持关键字参数,很多Web框架允许传入关键字参数,然后,在框架内部组装出一个作为Model。

    现在,我们把上次直接输出字符串作为HTML的例子用高端大气上档次的MVC模式改写一下:

     
    

    Flask通过函数来实现模板的渲染。和Web框架类似,Python的模板也有很多种。Flask默认支持的模板是jinja2,所以我们先直接安装jinja2:

     
    

    然后,开始编写jinja2模板:

    home.html

    用来显示首页的模板:

     
    
    form.html

    用来显示登录表单的模板:

     
    
    signin-ok.html

    登录成功的模板:

     
    

    登录失败的模板呢?我们在中加了一点条件判断,把重用为登录失败的模板。

    最后,一定要把模板放到正确的目录下,和在同级目录下:

    启动,看看使用模板的页面效果:

    通过MVC,我们在Python代码中处理M:Model和C:Controller,而V:View是通过模板处理的,这样,我们就成功地把Python代码和HTML代码最大限度地分离了。

    使用模板的另一大好处是,模板改起来很方便,而且,改完保存后,刷新浏览器就能看到最新的效果,这对于调试HTML、CSS和JavaScript的前端工程师来说实在是太重要了。

    在Jinja2模板中,我们用表示一个需要替换的变量。很多时候,还需要循环、条件判断等指令语句,在Jinja2中,用表示指令。

    比如循环输出页码:

     
    

    如果是一个list:,上面的模板将输出5个超链接。

    除了Jinja2,常见的模板还有:

    • Mako:用和的一个模板;
    • Cheetah:也是用和的一个模板;
    • Django:Django是一站式框架,内置一个用和的模板。
    小结

    有了MVC,我们就分离了Python代码和HTML代码。HTML代码全部放到模板里,写起来更有效率。

    源码参考

    app.py

    异步IO

    在IO编程一节中,我们已经知道,CPU的速度远远快于磁盘、网络等IO。在一个线程中,CPU执行代码的速度极快,然而,一旦遇到IO操作,如读写文件、发送网络数据时,就需要等待IO操作完成,才能继续进行下一步操作。这种情况称为同步IO。

    在IO操作的过程中,当前线程被挂起,而其他需要CPU执行的代码就无法被当前线程执行了。

    因为一个IO操作就阻塞了当前线程,导致其他代码无法执行,所以我们必须使用多线程或者多进程来并发执行代码,为多个用户服务。每个用户都会分配一个线程,如果遇到IO导致线程被挂起,其他用户的线程不受影响。

    多线程和多进程的模型虽然解决了并发问题,但是系统不能无上限地增加线程。由于系统切换线程的开销也很大,所以,一旦线程数量过多,CPU的时间就花在线程切换上了,真正运行代码的时间就少了,结果导致性能严重下降。

    由于我们要解决的问题是CPU高速执行能力和IO设备的龟速严重不匹配,多线程和多进程只是解决这一问题的一种方法。

    另一种解决IO问题的方法是异步IO。当代码需要执行一个耗时的IO操作时,它只发出IO指令,并不等待IO结果,然后就去执行其他代码了。一段时间后,当IO返回结果时,再通知CPU进行处理。

    可以想象如果按普通顺序写出的代码实际上是没法完成异步IO的:

     
    

    所以,同步IO模型的代码是无法实现异步IO模型的。

    异步IO模型需要一个消息循环,在消息循环中,主线程不断地重复“读取消息-处理消息”这一过程:

     
    

    消息模型其实早在应用在桌面应用程序中了。一个GUI程序的主线程就负责不停地读取消息并处理消息。所有的键盘、鼠标等消息都被发送到GUI程序的消息队列中,然后由GUI程序的主线程处理。

    由于GUI线程处理键盘、鼠标等消息的速度非常快,所以用户感觉不到延迟。某些时候,GUI线程在一个消息处理的过程中遇到问题导致一次消息处理时间过长,此时,用户会感觉到整个GUI程序停止响应了,敲键盘、点鼠标都没有反应。这种情况说明在消息模型中,处理一个消息必须非常迅速,否则,主线程将无法及时处理消息队列中的其他消息,导致程序看上去停止响应。

    消息模型是如何解决同步IO必须等待IO操作这一问题的呢?当遇到IO操作时,代码只负责发出IO请求,不等待IO结果,然后直接结束本轮消息处理,进入下一轮消息处理过程。当IO操作完成后,将收到一条“IO完成”的消息,处理该消息时就可以直接获取IO操作结果。

    在“发出IO请求”到收到“IO完成”的这段时间里,同步IO模型下,主线程只能挂起,但异步IO模型下,主线程并没有休息,而是在消息循环中继续处理其他消息。这样,在异步IO模型下,一个线程就可以同时处理多个IO请求,并且没有切换线程的操作。对于大多数IO密集型的应用程序,使用异步IO将大大提升系统的多任务处理能力。

    协程

    在学习异步IO模型前,我们先来了解协程。

    协程,又称微线程,纤程。英文名Coroutine。

    协程的概念很早就提出来了,但直到最近几年才在某些语言(如Lua)中得到广泛应用。

    子程序,或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。

    所以子程序调用是通过栈实现的,一个线程就是执行一个子程序。

    子程序调用总是一个入口,一次返回,调用顺序是明确的。而协程的调用和子程序不同。

    协程看上去也是子程序,但执行过程中,在子程序内部可中断,然后转而执行别的子程序,在适当的时候再返回来接着执行。

    注意,在一个子程序中中断,去执行其他子程序,不是函数调用,有点类似CPU的中断。比如子程序A、B:

     
    

    假设由协程执行,在执行A的过程中,可以随时中断,去执行B,B也可能在执行过程中中断再去执行A,结果可能是:

     
    

    但是在A中是没有调用B的,所以协程的调用比函数调用理解起来要难一些。

    看起来A、B的执行有点像多线程,但协程的特点在于是一个线程执行,那和多线程比,协程有何优势?

    最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。

    第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

    因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

    Python对协程的支持是通过generator实现的。

    在generator中,我们不但可以通过循环来迭代,还可以不断调用函数获取由语句返回的下一个值。

    但是Python的不但可以返回一个值,它还可以接收调用者发出的参数。

    来看例子:

    传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。

    如果改用协程,生产者生产消息后,直接通过跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:

     
    

    执行结果:

     
    

    注意到函数是一个,把一个传入后:

    1. 首先调用启动生成器;
    2. 然后,一旦生产了东西,通过切换到执行;
    3. 通过拿到消息,处理,又通过把结果传回;
    4. 拿到处理的结果,继续生产下一条消息;
    5. 决定不生产了,通过关闭,整个过程结束。

    整个流程无锁,由一个线程执行,和协作完成任务,所以称为“协程”,而非线程的抢占式多任务。

    最后套用Donald Knuth的一句话总结协程的特点:

    “子程序就是协程的一种特例。”

    参考源码

    coroutine.py

    asyncio

    是Python 3.4版本引入的标准库,直接内置了对异步IO的支持。

    的编程模型就是一个消息循环。我们从模块中直接获取一个的引用,然后把需要执行的协程扔到中执行,就实现了异步IO。

    用实现代码如下:

     
    

    把一个generator标记为coroutine类型,然后,我们就把这个扔到中执行。

    会首先打印出,然后,语法可以让我们方便地调用另一个。由于也是一个,所以线程不会等待,而是直接中断并执行下一个消息循环。当返回时,线程就可以从拿到返回值(此处是),然后接着执行下一行语句。

    把看成是一个耗时1秒的IO操作,在此期间,主线程并未等待,而是去执行中其他可以执行的了,因此可以实现并发执行。

    我们用Task封装两个试试:

     
    

    观察执行过程:

     
    

    由打印的当前线程名称可以看出,两个是由同一个线程并发执行的。

    如果把换成真正的IO操作,则多个就可以由一个线程并发执行。

    我们用的异步网络连接来获取sina、sohu和163的网站首页:

     
    

    执行结果如下:

     
    

    可见3个连接由一个线程通过并发完成。

    小结

    提供了完善的异步IO支持;

    异步操作需要在中通过完成;

    多个可以封装成一组Task然后并发执行。

    参考源码

    async_hello.py

    async_wget.py

    async/await

    用提供的可以把一个generator标记为coroutine类型,然后在coroutine内部用调用另一个coroutine实现异步操作。

    为了简化并更好地标识异步IO,从Python 3.5开始引入了新的语法和,可以让coroutine的代码更简洁易读。

    请注意,和是针对coroutine的新语法,要使用新的语法,只需要做两步简单的替换:

    1. 把替换为;
    2. 把替换为。

    让我们对比一下上一节的代码:

     
    

    用新语法重新编写如下:

     
    

    剩下的代码保持不变。

    小结

    Python从3.5版本开始为提供了和的新语法;

    注意新语法只能用在Python 3.5以及后续版本,如果使用3.4版本,则仍需使用上一节的方案。

    练习

    将上一节的异步获取sina、sohu和163的网站首页源码用新语法重写并运行。

    参考源码

    async_hello2.py

    async_wget2.py

    aiohttp

    可以实现单线程并发IO操作。如果仅用在客户端,发挥的威力不大。如果把用在服务器端,例如Web服务器,由于HTTP连接就是IO操作,因此可以用单线程+实现多用户的高并发支持。

    实现了TCP、UDP、SSL等协议,则是基于实现的HTTP框架。

    我们先安装:

     
    

    然后编写一个HTTP服务器,分别处理以下URL:

    • - 首页返回;
    • - 根据URL参数返回文本。

    代码如下:

     
    

    注意的初始化函数也是一个,则利用创建TCP服务。

    参考源码

    aio_web.py

    实战

    看完了教程,是不是有这么一种感觉:看的时候觉得很简单,照着教程敲代码也没啥大问题。

    于是准备开始独立写代码,就发现不知道从哪开始下手了。

    这种情况是完全正常的。好比学写作文,学的时候觉得简单,写的时候就无从下笔了。

    虽然这个教程是面向小白的零基础Python教程,但是我们的目标不是学到60分,而是学到90分。

    所以,用Python写一个真正的Web App吧!

    目标

    我们设定的实战目标是一个Blog网站,包含日志、用户和评论3大部分。

    很多童鞋会想,这是不是太简单了?

    比如webpy.org上就提供了一个Blog的例子,目测也就100行代码。

    但是,这样的页面:

    你拿得出手么?

    我们要写出用户真正看得上眼的页面,首页长得像这样:

    评论区:

    还有极其强大的后台管理页面:

    是不是一下子变得高端大气上档次了?

    项目名称

    必须是高端大气上档次的名称,命名为。

    项目计划

    项目计划开发周期为16天。每天,你需要完成教程中的内容。如果你觉得编写代码难度实在太大,可以参考一下当天在GitHub上的代码。

    第N天的代码在上。比如第1天就是:

    https://github.com/michaelliao/awesome-python3-webapp/tree/day-01

    以此类推。

    要预览的最终页面效果,请猛击:

    awesome.liaoxuefeng.com

    Day 1 - 搭建开发环境

    搭建开发环境

    首先,确认系统安装的Python版本是3.4.x:

     
    

    然后,用安装开发Web App需要的第三方库:

    异步框架aiohttp:

     
    

    前端模板引擎jinja2:

     
    

    MySQL 5.x数据库,从官方网站下载并安装,安装完毕后,请务必牢记root口令。为避免遗忘口令,建议直接把root口令设置为;

    MySQL的Python异步驱动程序aiomysql:

     
    
    项目结构

    选择一个工作目录,然后,我们建立如下的目录结构:

     
    

    创建好项目的目录结构后,建议同时建立git仓库并同步至GitHub,保证代码修改的安全。

    要了解git和GitHub的用法,请移步Git教程。

    开发工具

    自备,推荐用Sublime Text,请参考使用文本编辑器。

    参考源码

    day-01

    Day 2 - 编写Web App骨架

    由于我们的Web App建立在asyncio的基础上,因此用aiohttp写一个基本的:

     
    

    运行,Web App将在端口监听HTTP请求,并且对首页进行响应:

     
    

    这里我们简单地返回一个字符串,在浏览器中可以看到效果:

    这说明我们的Web App骨架已经搭好了,可以进一步往里面添加更多的东西。

    参考源码

    day-02

    Day 3 - 编写ORM

    在一个Web App中,所有数据,包括用户信息、发布的日志、评论等,都存储在数据库中。在awesome-python3-webapp中,我们选择MySQL作为数据库。

    Web App里面有很多地方都要访问数据库。访问数据库需要创建数据库连接、游标对象,然后执行SQL语句,最后处理异常,清理资源。这些访问数据库的代码如果分散到各个函数中,势必无法维护,也不利于代码复用。

    所以,我们要首先把常用的SELECT、INSERT、UPDATE和DELETE操作用函数封装起来。

    由于Web框架使用了基于asyncio的aiohttp,这是基于协程的异步模型。在协程中,不能调用普通的同步IO操作,因为所有用户都是由一个线程服务的,协程的执行速度必须非常快,才能处理大量用户的请求。而耗时的IO操作不能在协程中以同步的方式调用,否则,等待一个IO操作时,系统无法响应任何其他用户。

    这就是异步编程的一个原则:一旦决定使用异步,则系统每一层都必须是异步,“开弓没有回头箭”。

    幸运的是为MySQL数据库提供了异步IO的驱动。

    创建连接池

    我们需要创建一个全局的连接池,每个HTTP请求都可以从连接池中直接获取数据库连接。使用连接池的好处是不必频繁地打开和关闭数据库连接,而是能复用就尽量复用。

    连接池由全局变量存储,缺省情况下将编码设置为,自动提交事务:

     
    
    Select

    要执行SELECT语句,我们用函数执行,需要传入SQL语句和SQL参数:

     
    

    SQL语句的占位符是,而MySQL的占位符是,函数在内部自动替换。注意要始终坚持使用带参数的SQL,而不是自己拼接SQL字符串,这样可以防止SQL注入攻击。

    注意到将调用一个子协程(也就是在一个协程中调用另一个协程)并直接获得子协程的返回结果。

    如果传入参数,就通过获取最多指定数量的记录,否则,通过获取所有记录。

    Insert, Update, Delete

    要执行INSERT、UPDATE、DELETE语句,可以定义一个通用的函数,因为这3种SQL的执行都需要相同的参数,以及返回一个整数表示影响的行数:

     
    

    函数和函数所不同的是,cursor对象不返回结果集,而是通过返回结果数。

    ORM

    有了基本的和函数,我们就可以开始编写一个简单的ORM了。

    设计ORM需要从上层调用者角度来设计。

    我们先考虑如何定义一个对象,然后把数据库表和它关联起来。

     
    

    注意到定义在类中的、和是类的属性,不是实例的属性。所以,在类级别上定义的属性用来描述对象和表的映射关系,而实例属性必须通过方法去初始化,所以两者互不干扰:

     
    
    定义Model

    首先要定义的是所有ORM映射的基类:

     
    

    从继承,所以具备所有的功能,同时又实现了特殊方法和,因此又可以像引用普通字段那样写:

     
    

    以及和各种子类:

     
    

    映射的:

     
    

    注意到只是一个基类,如何将具体的子类如的映射信息读取出来呢?答案就是通过metaclass::

     
    

    这样,任何继承自Model的类(比如User),会自动通过ModelMetaclass扫描映射关系,并存储到自身的类属性如、中。

    然后,我们往Model类添加class方法,就可以让所有子类调用class方法:

     
    

    User类现在就可以通过类方法实现主键查找:

     
    

    往Model类添加实例方法,就可以让所有子类调用实例方法:

     
    

    这样,就可以把一个User实例存入数据库:

     
    

    最后一步是完善ORM,对于查找,我们可以实现以下方法:

    • findAll() - 根据WHERE条件查找;
    • findNumber() - 根据WHERE条件查找,但返回的是整数,适用于类型的SQL。

    以及和方法。

    所有这些方法都必须用装饰,变成一个协程。

    调用时需要特别注意:

     
    

    没有任何效果,因为调用仅仅是创建了一个协程,并没有执行它。一定要用:

     
    

    才真正执行了INSERT操作。

    最后看看我们实现的ORM模块一共多少行代码?累计不到300多行。用Python写一个ORM是不是很容易呢?

    参考源码

    day-03

    Day 4 - 编写Model

    有了ORM,我们就可以把Web App需要的3个表用表示出来:

     
    

    在编写ORM时,给一个Field增加一个参数可以让ORM自己填入缺省值,非常方便。并且,缺省值可以作为函数对象传入,在调用时自动计算。

    例如,主键的缺省值是函数,创建时间的缺省值是函数,可以自动设置当前日期和时间。

    日期和时间用类型存储在数据库中,而不是类型,这么做的好处是不必关心数据库的时区以及时区转换问题,排序非常简单,显示的时候,只需要做一个到的转换,也非常容易。

    初始化数据库表

    如果表的数量很少,可以手写创建表的SQL脚本:

     
    

    如果表的数量很多,可以从对象直接通过脚本自动生成SQL脚本,使用更简单。

    把SQL脚本放到MySQL命令行里执行:

     
    

    我们就完成了数据库表的初始化。

    编写数据访问代码

    接下来,就可以真正开始编写代码操作对象了。比如,对于对象,我们就可以做如下操作:

     
    

    可以在MySQL客户端命令行查询,看看数据是不是正常存储到MySQL里面了。

    参考源码

    day-04

    Day 5 - 编写Web框架

    在正式开始Web开发前,我们需要编写一个Web框架。

    已经是一个Web框架了,为什么我们还需要自己封装一个?

    原因是从使用者的角度来说,相对比较底层,编写一个URL的处理函数需要这么几步:

    第一步,编写一个用装饰的函数:

     
    

    第二步,传入的参数需要自己从中获取:

     
    

    最后,需要自己构造对象:

     
    

    这些重复的工作可以由框架完成。例如,处理带参数的URL可以这么写:

     
    

    处理参数可以通过关键字参数或者命名关键字参数接收:

     
    

    对于函数的返回值,不一定是对象,可以是、或。

    如果希望渲染模板,我们可以这么返回一个:

     
    

    因此,Web框架的设计是完全从使用者出发,目的是让使用者编写尽可能少的代码。

    编写简单的函数而非引入和还有一个额外的好处,就是可以单独测试,否则,需要模拟一个才能测试。

    @get和@post

    要把一个函数映射为一个URL处理函数,我们先定义:

     
    

    这样,一个函数通过的装饰就附带了URL信息。

    与定义类似。

    定义RequestHandler

    URL处理函数不一定是一个,因此我们用来封装一个URL处理函数。

    是一个类,由于定义了方法,因此可以将其实例视为函数。

    目的就是从URL函数中分析其需要接收的参数,从中获取必要的参数,调用URL函数,然后把结果转换为对象,这样,就完全符合框架的要求:

     
    

    再编写一个函数,用来注册一个URL处理函数:

     
    

    最后一步,把很多次注册的调用:

     
    

    变成自动扫描:

     
    

    定义如下:

     
    

    最后,在中加入、模板和自注册的支持:

     
    
    middleware

    是一种拦截器,一个URL在被某个函数处理前,可以经过一系列的的处理。

    一个可以改变URL的输入、输出,甚至可以决定不继续处理而直接返回。middleware的用处就在于把通用的功能从每个URL处理函数中拿出来,集中放到一个地方。例如,一个记录URL日志的可以简单定义如下:

     
    

    而这个把返回值转换为对象再返回,以保证满足的要求:

     
    

    有了这些基础设施,我们就可以专注地往模块不断添加URL处理函数了,可以极大地提高开发效率。

    参考源码

    day-05

    Day 6 - 编写配置文件

    有了Web框架和ORM框架,我们就可以开始装配App了。

    通常,一个Web App在运行时都需要读取配置文件,比如数据库的用户名、口令等,在不同的环境中运行时,Web App可以通过读取不同的配置文件来获得正确的配置。

    由于Python本身语法简单,完全可以直接用Python源代码来实现配置,而不需要再解析一个单独的或者等配置文件。

    默认的配置文件应该完全符合本地开发环境,这样,无需任何设置,就可以立刻启动服务器。

    我们把默认的配置文件命名为:

     
    

    上述配置文件简单明了。但是,如果要部署到服务器时,通常需要修改数据库的host等信息,直接修改不是一个好办法,更好的方法是编写一个,用来覆盖某些默认设置:

     
    

    把作为开发环境的标准配置,把作为生产环境的标准配置,我们就可以既方便地在本地开发,又可以随时把应用部署到服务器上。

    应用程序读取配置文件需要优先从读取。为了简化读取配置文件,可以把所有配置读取到统一的中:

     
    

    这样,我们就完成了App的配置。

    参考源码

    day-06

    Day 7 - 编写MVC

    现在,ORM框架、Web框架和配置都已就绪,我们可以开始编写一个最简单的MVC,把它们全部启动起来。

    通过Web框架的和ORM框架的Model支持,可以很容易地编写一个处理首页URL的函数:

     
    

    指定的模板文件是,其他参数是传递给模板的数据,所以我们在模板的根目录下创建:

     
    

    接下来,如果一切顺利,可以用命令行启动Web服务器:

     
    

    然后,在浏览器中访问。

    如果数据库的表什么内容也没有,你就无法在浏览器中看到循环输出的内容。可以自己在MySQL的命令行里给表添加几条记录,然后再访问:

    参考源码

    day-07

    Day 8 - 构建前端

    虽然我们跑通了一个最简单的MVC,但是页面效果肯定不会让人满意。

    对于复杂的HTML前端页面来说,我们需要一套基础的CSS框架来完成页面布局和基本样式。另外,jQuery作为操作DOM的JavaScript库也必不可少。

    从零开始写CSS不如直接从一个已有的功能完善的CSS框架开始。有很多CSS框架可供选择。我们这次选择uikit这个强大的CSS框架。它具备完善的响应式布局,漂亮的UI,以及丰富的HTML组件,让我们能轻松设计出美观而简洁的页面。

    可以从uikit首页下载打包的资源文件。

    所有的静态资源文件我们统一放到目录下,并按照类别归类:

     
    

    由于前端页面肯定不止首页一个页面,每个页面都有相同的页眉和页脚。如果每个页面都是独立的HTML模板,那么我们在修改页眉和页脚的时候,就需要把每个模板都改一遍,这显然是没有效率的。

    常见的模板引擎已经考虑到了页面上重复的HTML部分的复用问题。有的模板通过include把页面拆成三部分:

     
    

    这样,相同的部分和就可以共享。

    但是include方法不利于页面整体结构的维护。jinjia2的模板还有另一种“继承”方式,实现模板的复用更简单。

    “继承”模板的方式是通过编写一个“父模板”,在父模板中定义一些可替换的block(块)。然后,编写多个“子模板”,每个子模板都可以只替换父模板定义的block。比如,定义一个最简单的父模板:

     
    

    对于子模板,只需要把父模板的和替换掉:

     
    

    对于子模板,如法炮制:

     
    

    这样,一旦定义好父模板的整体布局和CSS样式,编写子模板就会非常容易。

    让我们通过uikit这个CSS框架来完成父模板的编写:

     
    

    定义的几个block作用如下:

    用于子页面定义一些meta,例如rss feed:

     
    

    覆盖页面的标题:

     
    

    子页面可以在标签关闭前插入JavaScript代码:

     
    

    子页面的content布局和内容:

     
    

    我们把首页改造一下,从继承一个:

     
    

    相应地,首页URL的处理函数更新如下:

     
    

    Blog的创建日期显示的是一个浮点数,因为它是由这段模板渲染出来的:

     
    

    解决方法是通过jinja2的filter(过滤器),把一个浮点数转换成日期字符串。我们来编写一个的filter,在模板里用法如下:

     
    

    filter需要在初始化jinja2时设置。相关代码如下:

     
    

    现在,完善的首页显示如下:

    参考源码

    day-08

    Day 9 - 编写API

    自从Roy Fielding博士在2000年他的博士论文中提出REST(Representational State Transfer)风格的软件架构模式后,REST就基本上迅速取代了复杂而笨重的SOAP,成为Web API的标准了。

    什么是Web API呢?

    如果我们想要获取一篇Blog,输入,就可以看到id为的Blog页面,但这个结果是HTML页面,它同时混合包含了Blog的数据和Blog的展示两个部分。对于用户来说,阅读起来没有问题,但是,如果机器读取,就很难从HTML中解析出Blog的数据。

    如果一个URL返回的不是HTML,而是机器能直接解析的数据,这个URL就可以看成是一个Web API。比如,读取,如果能直接返回Blog的数据,那么机器就可以直接读取。

    REST就是一种设计API的模式。最常用的数据格式是JSON。由于JSON能直接被JavaScript读取,所以,以JSON格式编写的REST风格的API具有简单、易读、易用的特点。

    编写API有什么好处呢?由于API就是把Web App的功能全部封装了,所以,通过API操作数据,可以极大地把前端和后端的代码隔离,使得后端代码易于测试,前端代码编写更简单。

    一个API也是一个URL的处理函数,我们希望能直接通过一个来把函数变成JSON格式的REST API,这样,获取注册用户可以用一个API实现如下:

     
    

    只要返回一个,后续的这个就可以把结果序列化为JSON并返回。

    我们需要对Error进行处理,因此定义一个,这种Error是指API调用时发生了逻辑错误(比如用户不存在),其他的Error视为Bug,返回的错误代码为。

    客户端调用API时,必须通过错误代码来区分API调用是否成功。错误代码是用来告诉调用者出错的原因。很多API用一个整数表示错误码,这种方式很难维护错误码,客户端拿到错误码还需要查表得知错误信息。更好的方式是用字符串表示错误代码,不需要看文档也能猜到错误原因。

    可以在浏览器直接测试API,例如,输入,就可以看到返回的JSON:

    参考源码

    day-09

    Day 10 - 用户注册和登录

    用户管理是绝大部分Web网站都需要解决的问题。用户管理涉及到用户注册和登录。

    用户注册相对简单,我们可以先通过API把用户注册这个功能实现了:

     
    

    注意用户口令是客户端传递的经过SHA1计算后的40位Hash字符串,所以服务器端并不知道用户的原始口令。

    接下来可以创建一个注册页面,让用户填写注册表单,然后,提交数据到注册用户的API:

     
    

    这样我们就把用户注册的功能完成了:

    用户登录比用户注册复杂。由于HTTP协议是一种无状态协议,而服务器要跟踪用户状态,就只能通过cookie实现。大多数Web框架提供了Session功能来封装保存用户状态的cookie。

    Session的优点是简单易用,可以直接从Session中取出用户登录信息。

    Session的缺点是服务器需要在内存中维护一个映射表来存储用户登录信息,如果有两台以上服务器,就需要对Session做集群,因此,使用Session的Web App很难扩展。

    我们采用直接读取cookie的方式来验证用户登录,每次用户访问任意URL,都会对cookie进行验证,这种方式的好处是保证服务器处理任意的URL都是无状态的,可以扩展到多台服务器。

    由于登录成功后是由服务器生成一个cookie发送给浏览器,所以,要保证这个cookie不会被客户端伪造出来。

    实现防伪造cookie的关键是通过一个单向算法(例如SHA1),举例如下:

    当用户输入了正确的口令登录成功后,服务器可以从数据库取到用户的id,并按照如下方式计算出一个字符串:

     
    

    当浏览器发送cookie到服务器端后,服务器可以拿到的信息包括:

    • 用户id
    • 过期时间
    • SHA1值

    如果未到过期时间,服务器就根据用户id查找用户口令,并计算:

     
    

    并与浏览器cookie中的MD5进行比较,如果相等,则说明用户已登录,否则,cookie就是伪造的。

    这个算法的关键在于SHA1是一种单向算法,即可以通过原始字符串计算出SHA1结果,但无法通过SHA1结果反推出原始字符串。

    所以登录API可以实现如下:

     
    

    对于每个URL处理函数,如果我们都去写解析cookie的代码,那会导致代码重复很多次。

    利用middle在处理URL之前,把cookie解析出来,并将登录用户绑定到对象上,这样,后续的URL处理函数就可以直接拿到登录用户:

     
    

    这样,我们就完成了用户注册和登录的功能。

    参考源码

    day-10

    Day 11 - 编写日志创建页

    在Web开发中,后端代码写起来其实是相当容易的。

    例如,我们编写一个REST API,用于创建一个Blog:

     
    

    编写后端Python代码不但很简单,而且非常容易测试,上面的API:本身只是一个普通函数。

    Web开发真正困难的地方在于编写前端页面。前端页面需要混合HTML、CSS和JavaScript,如果对这三者没有深入地掌握,编写的前端页面将很快难以维护。

    更大的问题在于,前端页面通常是动态页面,也就是说,前端页面往往是由后端代码生成的。

    生成前端页面最早的方式是拼接字符串:

     
    

    显然这种方式完全不具备可维护性。所以有第二种模板方式:

     
    

    ASP、JSP、PHP等都是用这种模板方式生成前端页面。

    如果在页面上大量使用JavaScript(事实上大部分页面都会),模板方式仍然会导致JavaScript代码与后端代码绑得非常紧密,以至于难以维护。其根本原因在于负责显示的HTML DOM模型与负责数据和交互的JavaScript代码没有分割清楚。

    要编写可维护的前端代码绝非易事。和后端结合的MVC模式已经无法满足复杂页面逻辑的需要了,所以,新的MVVM:Model View ViewModel模式应运而生。

    MVVM最早由微软提出来,它借鉴了桌面应用程序的MVC思想,在前端页面中,把Model用纯JavaScript对象表示:

     
    

    View是纯HTML:

     
    

    由于Model表示数据,View负责显示,两者做到了最大限度的分离。

    把Model和View关联起来的就是ViewModel。ViewModel负责把Model的数据同步到View显示出来,还负责把View的修改同步回Model。

    ViewModel如何编写?需要用JavaScript编写一个通用的ViewModel,这样,就可以复用整个MVVM模型了。

    好消息是已有许多成熟的MVVM框架,例如AngularJS,KnockoutJS等。我们选择Vue这个简单易用的MVVM框架来实现创建Blog的页面:

     
    

    初始化Vue时,我们指定3个参数:

    el:根据选择器查找绑定的View,这里是,就是id为的DOM,对应的是一个标签;

    data:JavaScript对象表示的Model,我们初始化为;

    methods:View可以触发的JavaScript函数,就是提交表单时触发的函数。

    接下来,我们在标签中,用几个简单的,就可以让Vue把Model和View关联起来:

     
    

    Form表单通过把提交表单的事件关联到方法。

    需要特别注意的是,在MVVM中,Model和View是双向绑定的。如果我们在Form中修改了文本框的值,可以在Model中立刻拿到新的值。试试在表单中输入文本,然后在Chrome浏览器中打开JavaScript控制台,可以通过访问单个属性,或者通过访问整个Model:

    如果我们在JavaScript逻辑中修改了Model,这个修改会立刻反映到View上。试试在JavaScript控制台输入,可以看到文本框的内容自动被同步了:

    双向绑定是MVVM框架最大的作用。借助于MVVM,我们把复杂的显示逻辑交给框架完成。由于后端编写了独立的REST API,所以,前端用AJAX提交表单非常容易,前后端分离得非常彻底。

    参考源码

    day-11

    Day 12 - 编写日志列表页

    MVVM模式不但可用于Form表单,在复杂的管理页面中也能大显身手。例如,分页显示Blog的功能,我们先把后端代码写出来:

    在中定义一个类用于存储分页信息:

     
    

    在中实现API:

     
    

    管理页面:

     
    

    模板页面首先通过API:拿到Model:

     
    

    然后,通过Vue初始化MVVM:

     
    

    View的容器是,包含一个table,我们用可以把Model的数组直接变成多行的:

     
    

    往Model的数组中增加一个Blog元素,table就神奇地增加了一行;把数组的某个元素删除,table就神奇地减少了一行。所有复杂的Model-View的映射逻辑全部由MVVM框架完成,我们只需要在HTML中写上指令,就什么都不用管了。

    可以把看成循环代码,所以,可以在一个内部引用循环变量。和指令分别用于生成文本和DOM节点属性。

    完整的Blog列表页如下:

    参考源码

    day-12

    Day 13 - 提升开发效率

    现在,我们已经把一个Web App的框架完全搭建好了,从后端的API到前端的MVVM,流程已经跑通了。

    在继续工作前,注意到每次修改Python代码,都必须在命令行先Ctrl-C停止服务器,再重启,改动才能生效。

    在开发阶段,每天都要修改、保存几十次代码,每次保存都手动来这么一下非常麻烦,严重地降低了我们的开发效率。有没有办法让服务器检测到代码修改后自动重新加载呢?

    Django的开发环境在Debug模式下就可以做到自动重新加载,如果我们编写的服务器也能实现这个功能,就能大大提升开发效率。

    可惜的是,Django没把这个功能独立出来,不用Django就享受不到,怎么办?

    其实Python本身提供了重新载入模块的功能,但不是所有模块都能被重新载入。另一种思路是检测目录下的代码改动,一旦有改动,就自动重启服务器。

    按照这个思路,我们可以编写一个辅助程序,让它启动,并时刻监控目录下的代码改动,有改动时,先把当前进程杀掉,再重启,就完成了服务器进程的自动重启。

    要监控目录文件的变化,我们也无需自己手动定时扫描,Python的第三方库可以利用操作系统的API来监控目录文件的变化,并发送通知。我们先用安装:

     
    

    利用接收文件变化的通知,如果是文件,就自动重启进程。

    利用Python自带的实现进程的启动和终止,并把输入输出重定向到当前进程的输入输出中:

     
    

    一共70行左右的代码,就实现了Debug模式的自动重新加载。用下面的命令启动服务器:

     
    

    或者给加上可执行权限,启动服务器:

     
    

    在编辑器中打开一个文件,修改后保存,看看命令行输出,是不是自动重启了服务器:

     
    

    现在,只要一保存代码,就可以刷新浏览器看到效果,大大提升了开发效率。

    Day 14 - 完成Web App

    在Web App框架和基本流程跑通后,剩下的工作全部是体力活了:在Debug开发模式下完成后端所有API、前端所有页面。我们需要做的事情包括:

    把当前用户绑定到上,并对URL进行拦截,检查当前用户是否是管理员身份:

     
    

    后端API包括:

    • 获取日志:GET /api/blogs
    • 创建日志:POST /api/blogs
    • 修改日志:POST /api/blogs/:blog_id
    • 删除日志:POST /api/blogs/:blog_id/delete
    • 获取评论:GET /api/comments
    • 创建评论:POST /api/blogs/:blog_id/comments
    • 删除评论:POST /api/comments/:comment_id/delete
    • 创建新用户:POST /api/users
    • 获取用户:GET /api/users

    管理页面包括:

    • 评论列表页:GET /manage/comments
    • 日志列表页:GET /manage/blogs
    • 创建日志页:GET /manage/blogs/create
    • 修改日志页:GET /manage/blogs/
    • 用户列表页:GET /manage/users

    用户浏览页面包括:

    • 注册页:GET /register
    • 登录页:GET /signin
    • 注销页:GET /signout
    • 首页:GET /
    • 日志详情页:GET /blog/:blog_id

    把所有的功能实现,我们第一个Web App就宣告完成!

    参考源码

    day-14

    Day 15 - 部署Web App

    作为一个合格的开发者,在本地环境下完成开发还远远不够,我们需要把Web App部署到远程服务器上,这样,广大用户才能访问到网站。

    很多做开发的同学把部署这件事情看成是运维同学的工作,这种看法是完全错误的。首先,最近流行DevOps理念,就是说,开发和运维要变成一个整体。其次,运维的难度,其实跟开发质量有很大的关系。代码写得垃圾,运维再好也架不住天天挂掉。最后,DevOps理念需要把运维、监控等功能融入到开发中。你想服务器升级时不中断用户服务?那就得在开发时考虑到这一点。

    下面,我们就来把awesome-python3-webapp部署到Linux服务器。

    搭建Linux服务器

    要部署到Linux,首先得有一台Linux服务器。要在公网上体验的同学,可以在Amazon的AWS申请一台EC2虚拟机(免费使用1年),或者使用国内的一些云服务器,一般都提供Ubuntu Server的镜像。想在本地部署的同学,请安装虚拟机,推荐使用VirtualBox。

    我们选择的Linux服务器版本是Ubuntu Server 14.04 LTS,原因是apt太简单了。如果你准备使用其他Linux版本,也没有问题。

    Linux安装完成后,请确保ssh服务正在运行,否则,需要通过apt安装:

     
    

    有了ssh服务,就可以从本地连接到服务器上。建议把公钥复制到服务器端用户的中,这样,就可以通过证书实现无密码连接。

    部署方式

    利用Python自带的asyncio,我们已经编写了一个异步高性能服务器。但是,我们还需要一个高性能的Web服务器,这里选择Nginx,它可以处理静态资源,同时作为反向代理把动态请求交给Python代码处理。这个模型如下:

    Nginx负责分发请求:

    在服务器端,我们需要定义好部署的目录结构:

     
    

    在服务器上部署,要考虑到新版本如果运行不正常,需要回退到旧版本时怎么办。每次用新的代码覆盖掉旧的文件是不行的,需要一个类似版本控制的机制。由于Linux系统提供了软链接功能,所以,我们把作为一个软链接,它指向哪个目录,哪个目录就是当前运行的版本:

    而Nginx和gunicorn的配置文件只需要指向目录即可。

    Nginx可以作为服务进程直接启动,但gunicorn还不行,所以,Supervisor登场!Supervisor是一个管理进程的工具,可以随系统启动而启动服务,它还时刻监控服务进程,如果服务进程意外退出,Supervisor可以自动重启服务。

    总结一下我们需要用到的服务有:

    • Nginx:高性能Web服务器+负责反向代理;
    • Supervisor:监控服务进程的工具;
    • MySQL:数据库服务。

    在Linux服务器上用apt可以直接安装上述服务:

     
    

    然后,再把我们自己的Web App用到的Python库安装了:

     
    

    在服务器上创建目录以及相应的子目录。

    在服务器上初始化MySQL数据库,把数据库初始化脚本复制到服务器上执行:

     
    

    服务器端准备就绪。

    部署

    用FTP还是SCP还是rsync复制文件?如果你需要手动复制,用一次两次还行,一天如果部署50次不但慢、效率低,而且容易出错。

    正确的部署方式是使用工具配合脚本完成自动化部署。Fabric就是一个自动化部署工具。由于Fabric是用Python 2.x开发的,所以,部署脚本要用Python 2.7来编写,本机还必须安装Python 2.7版本。

    要用Fabric部署,需要在本机(是开发机器,不是Linux服务器)安装Fabric:

     
    

    Linux服务器上不需要安装Fabric,Fabric使用SSH直接登录服务器并执行部署命令。

    下一步是编写部署脚本。Fabric的部署脚本叫,我们把它放到的目录下,与目录平级:

     
    

    Fabric的脚本编写很简单,首先导入Fabric的API,设置部署时的变量:

     
    

    然后,每个Python函数都是一个任务。我们先编写一个打包的任务:

     
    

    Fabric提供来运行本地命令,可以把当前命令的目录设定为指定的目录,注意Fabric只能运行命令行命令,Windows下可能需要Cgywin环境。

    在目录下运行:

     
    

    看看是否在目录下创建了的文件。

    打包后,我们就可以继续编写任务,把打包文件上传至服务器,解压,重置软链接,重启相关服务:

     
    

    注意函数执行的命令是在服务器上运行,和类似,把当前目录在服务器端设置为指定的目录。如果一个命令需要sudo权限,就不能用,而是用来执行。

    配置Supervisor

    上面让Supervisor重启awesome的命令会失败,因为我们还没有配置Supervisor呢。

    编写一个Supervisor的配置文件,存放到目录下:

     
    

    配置文件通过指定服务名为,指定启动。

    然后重启Supervisor后,就可以随时启动和停止Supervisor管理的服务了:

     
    
    配置Nginx

    Supervisor只负责运行gunicorn,我们还需要配置Nginx。把配置文件放到目录下:

     
    

    然后在目录下创建软链接:

     
    

    让Nginx重新加载配置文件,不出意外,我们的应该正常运行:

     
    

    如果有任何错误,都可以在下查找Nginx和App本身的log。如果Supervisor启动时报错,可以在下查看Supervisor的log。

    如果一切顺利,你可以在浏览器中访问Linux服务器上的了:

    如果在开发环境更新了代码,只需要在命令行执行:

     
    

    自动部署完成!刷新浏览器就可以看到服务器代码更新后的效果。

    友情链接

    嫌国外网速慢的童鞋请移步网易和搜狐的镜像站点:

    http://mirrors.163.com/

    http://mirrors.sohu.com/

    参考源码

    day-15

    Day 16 - 编写移动App

    网站部署上线后,还缺点啥呢?

    在移动互联网浪潮席卷而来的今天,一个网站没有上线移动App,出门根本不好意思跟人打招呼。

    所以,必须得有一个移动App版本!

    开发iPhone版本

    我们首先来看看如何开发iPhone App。前置条件:一台Mac电脑,安装XCode和最新的iOS SDK。

    在使用MVVM编写前端页面时,我们就能感受到,用REST API封装网站后台的功能,不但能清晰地分离前端页面和后台逻辑,现在这个好处更加明显,移动App也可以通过REST API从后端拿到数据。

    我们来设计一个简化版的iPhone App,包含两个屏幕:列出最新日志和阅读日志的详细内容:

    只需要调用API:。

    在XCode中完成App编写:

    由于我们的教程是Python,关于如何开发iOS,请移步Develop Apps for iOS。

    点击下载iOS App源码。

    如何编写Android App?这个当成作业了。

    参考源码

    day-16

    FAQ

    常见问题

    本节列出常见的一些问题。

    如何获取当前路径

    当前路径可以用表示,再用将其转换为绝对路径:

     
    

    运行结果:

     
    
    如何获取当前模块的文件名

    可以通过特殊变量获取:

     
    

    输出:

     
    
    如何获取命令行参数

    可以通过模块的获取:

     
    

    输出:

     
    

    的第一个元素永远是命令行执行的文件名。

    如何获取当前Python命令的可执行文件路径

    模块的变量就是Python命令可执行文件的路径:

     
    

    在Mac下的结果:

     
    

    期末总结

    终于到了期末总结的时刻了!

    经过一段时间的学习,相信你对Python已经初步掌握。一开始,可能觉得Python上手很容易,可是越往后学,会越困难,有的时候,发现理解不了代码,这时,不妨停下来思考一下,先把概念搞清楚,代码自然就明白了。

    Python非常适合初学者用来进入计算机编程领域。Python属于非常高级的语言,掌握了这门高级语言,就对计算机编程的核心思想——抽象有了初步理解。如果希望继续深入学习计算机编程,可以学习C、JavaScript、Lisp等不同类型的语言,只有多掌握不同领域的语言,有比较才更有收获。

    小讯
    上一篇 2025-01-01 07:12
    下一篇 2025-01-02 12:42

    相关推荐

    版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
    如需转载请保留出处:https://51itzy.com/kjqy/6304.html