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解释器干了两件事情:
- 在内存中创建了一个的字符串;
- 在内存中创建了一个名为的变量,并把它指向。
也可以把一个变量赋值给另一个变量,这个操作实际上是把变量指向变量所指向的数据,例如下面的代码:
最后一行打印出变量的内容到底是呢还是?如果从数学意义上理解,就会错误地得出和相同,也应该是,但实际上的值是,让我们一行一行地执行代码,就可以看到到底发生了什么事:
执行,解释器创建了字符串和变量,并把指向:
执行,解释器创建了变量,并把指向指向的字符串:
执行,解释器创建了字符串'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>
br>
不可变对象
。
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>

只读
属性,因为可以根据和当前时间计算出来。
br>
br>
br>
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开发也经历了好几个阶段:
- 静态Web页面:由文本编辑器直接编辑并生成静态的HTML页面,如果要修改Web页面的内容,就需要再次编辑HTML源文件,早期的互联网Web页面就是静态的;
- CGI:由于静态Web页面无法与用户交互,比如用户填写了一个注册表单,静态Web页面就无法处理。要处理用户发送的动态数据,出现了Common Gateway Interface,简称CGI,用C/C++编写。
- ASP/JSP/PHP:由于Web应用特点是修改频繁,用C/C++这样的低级语言非常不适合Web开发,而脚本语言由于开发效率高,与HTML结合紧密,因此,迅速取代了CGI模式。ASP是微软推出的用VBScript脚本编程的Web开发技术,而JSP用Java来编写脚本,PHP本身则是开源的脚本语言。
- 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应用的本质就是:
- 浏览器发送一个HTTP请求;
- 服务器收到请求,生成一个HTML文档;
- 服务器把HTML文档作为HTTP响应的Body发送给浏览器;
- 浏览器收到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的不但可以返回一个值,它还可以接收调用者发出的参数。
来看例子:
传统的生产者-消费者模型是一个线程写消息,一个线程取消息,通过锁机制控制队列和等待,但一不小心就可能死锁。
如果改用协程,生产者生产消息后,直接通过跳转到消费者开始执行,待消费者执行完毕后,切换回生产者继续生产,效率极高:
执行结果:
注意到函数是一个,把一个传入后:
- 首先调用启动生成器;
- 然后,一旦生产了东西,通过切换到执行;
- 通过拿到消息,处理,又通过把结果传回;
- 拿到处理的结果,继续生产下一条消息;
- 决定不生产了,通过关闭,整个过程结束。
整个流程无锁,由一个线程执行,和协作完成任务,所以称为“协程”,而非线程的抢占式多任务。
最后套用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的新语法,要使用新的语法,只需要做两步简单的替换:
- 把替换为;
- 把替换为。
让我们对比一下上一节的代码:
用新语法重新编写如下:
剩下的代码保持不变。
小结
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等不同类型的语言,只有多掌握不同领域的语言,有比较才更有收获。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/6304.html