第2章 开启Python之旅本章主要介绍Python的基础知识,为后续章节学习相关内容做铺垫。2.1 认识程序旅行前,我们要先熟悉地名,知道去哪里旅行,将会经过哪里。学习编程语言也一样,在学习之前要先了解程序、调试、语法错误、运行错误、语义错误等知识。2.1.1 程序我们都知道,出门旅行肯定要选择交通工具,现在常用的交通工具有飞机、火车、轮船、汽车等,我们会根据自己的喜好和一些其他因素选择对应的交通工具。编程语言也一样,我们选择一门编程语言就相当于选择一种交通工具,那么,编程语言的交通工具是什么呢?是程序。程序是指根据语言提供的指令按照一定逻辑顺序对获得的数据进行运算,并最终返回给我们的指令和数据的组合。在这里,运算的含义是广泛的,既包括数学计算之类的操作(如加减乘除),又包括寻找和替换字符串之类的操作。数据依据不同的需要组成不同的形式,处理后的数据也可能以另一种方式体现。程序是用语言写成的。语言分高级语言和低级语言。低级语言有时叫机器语言或汇编语言。计算机真正认识并能够执行的代码,在我们看来是一串0和1组成的二进制数字,这些数字代表指令和数据。早期的计算机科学家就是用这些枯燥乏味的数字编程。低级语言的出现是计算机程序语言的一大进步,它用英文单词或单词的缩写代表计算机执行的指令,使编程的效率和程序的可读性都有了很大提高,但它仍然和机器硬件关联紧密,不符合人类的语言和思维习惯,而且要想把用低级语言写的程序移植到其他平台,就必须重写。高级语言的出现是程序语言发展的必然结果,也是计算机语言向人类的自然语言和思维方式逐步靠近和模拟的结果。由于高级语言是对人类逻辑思维的描述,用高级语言写程序会感到比较自然,读起来也比较容易,因此现在大部分程序都是用高级语言写的。高级语言设计的目的是让程序按照人类的思维和语言习惯书写,是面向人的,而不是面向机器。我们用着方便,机器却无法读懂,更谈不上运行。所以,用高级语言写的程序必须经过翻译程序的处理,将其转换成机器可执行的代码,才能运行在计算机上。如果想把高级语言写的程序移植到其他的平台,只需在它的基础上做少量更改就可以了。高级语言翻译成机器代码有两种方法,即解释和编译。解释型语言是边读源程序边执行。高级语言就是源代码。解释器每次会读入一段源代码,并执行它,接着再读入并执行,如此重复,直到结束,图2-1显示了解释器的结构。这个有点类似在乡村里搭乘公交,只要碰到路上有人等公交,就停下来载人。编译型语言是将源代码完整的编译成目标代码后才能执行,以后在执行时不需要再编译。图2-2显示了一个编译器的结构,这个有点类似我们乘坐的直达车,所有要乘车的人都从起点上车,中途不再搭载其他乘客。图2-1 解释型语言的执行方式图2-2 编译型语言的执行方式2.1.2 调试每当远游时,司机肯定要做几件事情,如检查发动机是否正常、检查油箱、检查各项安全系统和液压系统等,为的是尽可能减少在路途中发生意外情况。我们编程也是一样的,需要经常做检查。有一些问题编译器会帮助我们检查出来,问题查出后,简单的可以直接解决,对于稍微复杂的,需要通过调试来解决。程序是很容易出错的。程序错误被称为bug,查找bug的过程称为调试(debugging)。我们在第1章中已经介绍过一个很简单的调试示例。2.1.3 语法错误在生活中有时会碰到这样的情况,你本应买今天去某地的火车票,已经买好了,但当进站检票时,系统告诉你票不是今天的,这时你才发现购买车票时输错了日期。程序中这种错误比生活中出现的次数多很多,称为语法错误(syntax errors)。Python程序在语法正确的情况下才能运行,否则解释器会显示一条错误信息。语法指的是程序的结构和此结构的规则。比如第1章的''Hello,world!'',括号中的单引号是成对的,执行时才能正确执行。如果输入''Hello,world!或Hello,world!''就会报错,这属于语法错误。我们在阅读文章或听人讲话时,可以容忍大多数语法错误,不过Python并不如此宽容。程序中只要出现一处语法错误,Python就会显示错误信息并退出,从而无法通过编译。就如我们进站,一旦票不满足进站要求,就无法进入一样。在编程生涯的开始阶段,可能每踏出一步都会碰到大量语法方面的错误,随着经验的增加,犯错会减少,踩过坑后,后面遇到类似的坑就能快速应对了。2.1.4 运行错误我们可能在奔跑的交通工具内愉悦地欣赏着远途的风景,但此时交通工具突然慢慢停下来了,并且司机对大家宣布说,交通工具抛锚了。例如,出现轮胎破损、没油了、发动机坏了、撞车事故等。在Python中经常会遇到类似的错误,称之为运行时错误(runtime errors)。即使是看起来完美无缺的程序,在运行的过程中也会出现错误。有人说,计算机不是善于精确计算吗?确实如此。不过错的不是计算机,而是我们人类。计算机说到底是人类设计的,是我们所用的工具,和电视机、汽车从本质上来说是一样的。鉴于现在计算机软硬件的理论水平、工业制造水平、使用者的水平等一些内在、外在的因素,出现错误并不稀奇,且程序越复杂,出现异常的概率越大。异常的种类很多,如内存用尽、除数为零的除法等都可能导致异常。Python为了把错误的影响降至最低,提供了专门的异常处理语句。运行时错误一般在代码量偏多时才容易遇到。2.1.5 语义错误经常乘坐交通工具,难免有乘错车的情况出现,比如你本应该乘坐801路车,却坐上了802路车,结果到达了不同的地方。在Python中经常会发生类似的问题,此类问题称为语义错误(semantic errors)。程序即使有语义错误,也能正常运行,不会产生任何错误信息,但得到的结果和我们预料的不一致。发生这种错误一般是我们对语句的运行机制了解得不够透彻,自以为它应该如此运行,实际上却不是这样。还有可能是你解决问题的思路本身就是错的,写的程序当然是错的。查找语义错误并不像我们坐错了车那么容易,它可能需要你根据结果进行推理,不过推理的过程没有那么简单易行,需要查看程序输出,并尝试弄明白到底做了什么。2.2 数据类型计算机是可以做数学计算的机器,计算机程序理所当然可以处理各种数值。计算机能处理的远不止数值,还有文本、图形、音频、视频、网页等各种各样的数据,不同的数据需要定义不同的数据类型。Python 3中有6种标准的数据类型:Number(数字)、String(字符串)、List(列表)、Tuple(元组)、Sets(集合)、Dictionary(字典)。本节首先讲解Number数值类型,其他5种数据类型在后续章节介绍。Python 3支持3种不同的数值类型:整型(int)、浮点型(float)、复数(complex)。2.2.1 整型int通常被称为整型或整数,是正、负整数,不带小数点。在Python 3中,整型没有限制大小,可以当作long类型使用,所以Python 3没有Python 2的long类型。例如,公司组织旅游,大家坐上了大巴准备出发,现在需要统计有多少人在车上,boss吩咐小萌清点一下人数,小萌花了两分钟逐个点了一遍,总计51人。小萌在交互模式下输入: 5151这里使用的就是整形。到服务区后,大家休息了一下,再次准备出发时,boss又吩咐小萌清点一下人数。小萌苦笑一下,看来又得花两分钟清点人数了,为什么不叫一个人帮忙从车的另一头清点呢?于是小萌叫小智帮忙从另一头清点一下人数。一分钟后,小萌和小智在车中间碰上了,小智告诉小萌他的计数是25人,小萌在交互模式下输入: 25 2550小萌准备把数字报告给boss,突然想到上次报告的是51人,这次是50人,数字不同啊,还差1人,小萌在交互模式下输入: 51-501怎么少了一人呢?小萌突然慌了,然后仔细一想,原来是把自己忘加上了,于是再次输入: 25 25 151这次没问题了。于是小萌给了boss和上次一样的答案。boss示意司机可以发车了,又突然叫停,看天气挺热的,大家路上可能会口渴,于是吩咐小萌去服务区给每人买两瓶水,再买一大包糖给大家在路上补充能量。每人两瓶水,一共要买多少瓶呢?小萌在交互模式下输入: 51*2102一共要买102瓶水,这么多,于是小萌让小智帮忙一起去提水。水和糖都买回来了,水好分,给每人两瓶就是,这一大包糖该怎么给大家呢?看包装袋上有总颗数,一共有153颗,每人多少颗呢?小萌在交互模式下输入: 153513.0好了,给每人发3颗糖就可以了。于是小萌高高兴兴发糖去了,小智也帮忙一起发,每人给3颗。糖终于发完了,小萌很惬意,坐下来补充能量。小萌突然想到了什么,有153颗糖,分给51人,每人3颗糖没错,但计算出来的结果怎么是3.0呢?假如有155颗糖,计算结果会是怎样的呢?输入以下数据: 155513.0392156862745097如果按这个结果分,就没有办法分了。这是怎么回事呢?原因是:在整数除法中,除法()计算结果是浮点数,即使两个整数恰好整除,结果也是浮点数。如果只想得到整数的结果,丢弃可能的分数部分,可以使用地板除(),整数的地板除()永远是整数,即使除不尽。更改前面输入的数据: 153513这时就不是浮点数了。再看看155颗糖的结果: 155513153和155除以51都是3,这个就不对了。原来还有一个余数。因为地板除()只取结果的整数部分,所以Python提供了一个余数运算,可以得到两个整数相除的余数,看看153和155对51的取余: 153%510 155%512这次的结果就是想要的了。假如有155颗糖,就会多出2颗。小萌细细想着,嘴角泛起一丝自然的微笑,小智也发完糖了,过来向小萌汇报,看见小萌的表情,小智心里顿时暖暖的,无须报告了。2.2.2 浮点型浮点型由整数部分与小数部分组成,也可以使用科学计数法表示。比如,小萌还在细想中,boss突然打断了她的思维,问她总共花了多少钱。小萌理了一下思绪,每瓶水3.3元,一共102瓶,总共多少钱呢?输入如下: 3.3*102336.59999999999997结果怎么这么长?小萌看傻了,不过冷静一看,原来是:整数和浮点数在计算机内部存储的方式不同,整数运算永远是精确的,而浮点数运算可能会有四舍五入的误差。boss,一共336.6元。好的,加上买糖的钱了吗?小萌暗骂自己,瞧我这脑筋。再计算一次,每瓶水3.3元,一共102瓶,再加上一包糖15.5元,输入如下: 3.3*102 15.5352.09999999999997这结果好像很凌乱,应该这么输入: 336.6 15.5352.1这个结果就好看多了。报告给boss,一共花费352.1元。小萌又开始思考了,浮点数相乘结果这么怪,浮点数的除法是怎样的呢?想到了就实践,输入如下: 15351.03.0这个结果和153除以51的结果是一样的,如果155除以51.0呢?输入如下: 15551.03.0392156862745097结果和155除以51也是一样的。那做地板除和取余又是怎样的呢?输入如下: 15551.03.0 155%51.02.0可以看出,得到的结果都是浮点型的。2.2.3 复数复数由实数部分和虚数部分构成,可以用a bj或complexa,b表示,复数的实部a和虚部b都是浮点型。Python支持复数,不过Python的复数我们当前阶段使用或接触的比较少,此处就不再具体讲解,读者有一个概念即可,有兴趣可以自行查阅相关资料。2.2.4 数据类型转换有时我们要对数据内置的类型进行转换,只需要将数据类型作为函数名即可。数据的类型转换时有如下4个函数可以使用:? intx 将x转换为一个整数。? floatx 将x转换为一个浮点数。? complexx 将x转换为一个复数,实数部分为 x,虚数部分为 0。? complexx, y 将x和y转换为一个复数,实数部分为x,虚数部分为y。x和y是数字表达式。比如,小萌去购物,计算出应付金额是352.1元,超市老板为免除找零的麻烦,让小萌支付352元。Python中的转换可以如下执行: int352.1352很容易就得到了转换后的结果。但是金额的操作必须用浮点数进行记账,这个容易,用float函数就行了。输入如下: float352.1352.1这样就得到了浮点型数据。结果跟输入的一样,这下怎么办呢?把int函数放入float函数中是否可以呢?尝试一下: floatint352.1352.0这里先把352.1取整,得到整数352,再用float将352转换成浮点数352.0,这样就得到我们需要的结果了。虽然输入的字符看起来有点复杂,但是还是得到结果了。这其实是函数的嵌套,后面会进行具体介绍,此处做相关了解即可。2.2.5 常量所谓常量,就是不能变的变量,比如常用的数学常数就是一个常量。在Python中,通常用全部大写的变量名表示常量。Python中有两个比较常见的常量,即PI和E。? PI:数学常量pi(圆周率,一般以表示)。? E:数学常量e,即自然常数。这两个常量将会在后续章节中使用,具体的用法在使用中体现。2.3 变量和关键字编程语言最强大的功能之一是操纵变量。变量(variable)是一个需要熟知的概念,如果你觉得数学让你抓狂,别担心,Python中的变量很好理解。变量基本上代表某个值的名字。2.3.1 变量变量指向各种类型值的名字,以后再用到这个值时,直接引用名字即可,不用再写具体的值。在Python中,变量的使用环境非常宽松,没有明显的变量声明,而且类型不是固定的。你可以把一个整数赋值给变量,如果觉得不合适,把字符串赋给变量也可以。在Python中,等号(=)是赋值语句,可以把任意数据类型赋值给变量。如果要定义一个名为xiaohong的变量,该怎么操作呢?输入如下: xiaohong=''XiaoHong''字符串必须以引号标记开始,并以引号标记结束。此操作解释:xiaohong是我们创建的变量,=是赋值语句,XiaoHong是变量值,需要用引号标记。整句话的意思为:创建变量xiaohong并赋值为XiaoHong。小萌疑惑了,怎么前面输入后按回车键就能输出内容,这里怎么跳到输入提示状态呢?别急,还记得前面讲的print吗?print是输出函数,看看这里,没有使用输出函数,屏幕上当然不会输出内容了。要输出内容应该怎么操作呢?我们尝试一下: printxiaohongXiaoHong不错,尝试成功了。但为什么小萌输入的是printxiaohong,结果却输出XiaoHong呢?这就是变量的好处,可以只定义一个变量,比如xiaohong,把一个值赋给这个变量,比如XiaoHong,这样当我们输出xiaohong时,实际上输出的是XiaoHong。在使用变量前需要对其赋值。没有值的变量是没有意义的,编译器也不会编译通过。例如,定义一个变量为abc,不赋任何值,输入及结果如下: abcTraceback most recent call last: File "", line 1, in abcNameError: name ''abc'' is not defined同一个变量可以反复赋值,而且可以是不同类型的变量,输入如下: a = 123123 a=''ABC'' printaABC这种变量本身类型不固定的语言称为动态语言,与之对应的是静态语言。静态语言在定义变量时必须指定变量类型,如果赋值时类型不匹配就会报错。和静态语言相比,动态语言更灵活。当不能确定变量或数据的类型时,可以借助解释器内置的函数type进行确认。在交互模式下可以如下输入: type''Hello,world!''测试得到输入的结果类型是字符串类型(str)。 type100测试得到输入的结果类型是整型(int)。 type3.0测试得到输入的结果类型是浮点型(float)。 a=''test type'' typea测试得到输入的结果类型是字符串类型(str)。 b=100 typeb测试得到输入的结果类型是整型(int)。 c=3.0 typec测试得到输入的结果类型是浮点型(float)。只要是用双引号或单引号括起来的值,都属于字符串。交互模式下输入: type''test single quotes'' type"test double quote" type"100" type"3.0" b=3 typeb b=100 typeb c=3.0 typec测试得到输入的结果类型都是字符串类型(str)。注意不要把赋值语句的等号等同于数学中的等号。比如下面的代码:a = 100a = a 200小萌一脸疑问,a = a 200是什么等式啊?读书时不知道在这个等式上花费了多少脑细胞,可别再拿这个骗我了,我可是读过小学的。小萌啊小萌,计算机可不是人脑,也不需要遵循学校的知识,计算机有计算机的思考方式。在编程语言中,赋值语句先计算右侧的表达式a 200,得到结果300,再赋给变量a。由于a之前的值是100,重新赋值后,a的值变成300。我们通过交互模式验证,输入如下: a=100 a=a 200 printa300和我们所描述的结果一致。理解变量在计算机内存中的表示也非常重要。当我们输入如下: a=''ABC''这时,Python解释器做了两件事情:(1)在内存中创建了一个''ABC''字符串。(2)在内存中创建了一个名为a的变量,并把它指向''ABC''。也可以把一个变量a赋值给另一个变量b,这个操作实际上是把变量b指向变量a所指向的数据,例如下面的代码: a=''ABC'' b=a a=''XYZ'' printb最后一行输出变量b的内容到底是''ABC''还是''XYZ''呢?如果从数学意义上理解,就会错误地得出b和a相同,应该是''XYZ''。实际上,b的值是''ABC''。我们一行一行执行代码,就可以看到到底发生了什么事。执行a = ''ABC'',解释器创建了字符串''ABC''和变量a,并把a指向''ABC'',如图2-3所示。图2-3 a指向''ABC''执行b = a,解释器创建了变量b,并把b指向a指向的字符串''ABC'',如图2-4所示。图2-4 a、b指向''ABC''执行a = ''XYZ'',解释器创建了字符串''XYZ'',并把a的指向改为''XYZ'',但b没有更改,如图2-5所示。图2-5 a指向''XYZ'',b不变最后输出变量b的结果自然是''ABC''了。2.3.2 变量名称程序常常选择有意义的名称作为变量名,以此标记变量的用途。变量名是由数字或字符组成的任意长度的字符串,必须以字母开头。使用大写字母是合法的,但变量名建议使用小写字母开头。Python是区分大小写的。举个例子来说,Name和name是两个不同的变量名。交互模式中输入如下: name=''study python is happy'' Name=''I aggree with you'' printnamestudy python is happy printNameI aggree with you下划线_可以出现在变量名中,经常用于连接多个词组,如happy_study, do_it_with_ more_practice。交互模式输入如下: happy_study=''stay hungry stay foolish'' printhappy_studystay hungry stay foolish如果给变量取非法的名称,解释器就会显示语法错误。请看下面的示例: 2wrongtest=''just for test''SyntaxError: invalid syntax该示例提示语法错误,错误信息为无效的语法,原因为不是以字母开头的。 xiaoming@me=''surprised''SyntaxError: can''t assign to operator该示例提示语法错误,错误信息为不能做指定操作,原因是包含一个非法字符@。Python不允许使用关键字作为变量名,请看下面的例子: from=''from''SyntaxError: invalid syntaxfrom是Python的一个关键字,因此出现错误。Python 3中共有33个关键字,都不能作为变量名来使用。False NoneTrue and as assertbreakclass continue def del elifelse exceptfinally for from globalif importinnonlocallambdaisnot or pass raisereturn trywhilewith yield2.4 语句语句是Python解释器可以运行的一个代码单元,也可以理解为可以执行的命令。我们目前已经使用了两种语句:print打印语句和赋值语句。赋值语句有两个作用:一是建立新的变量,二是将值赋予变量。任何变量在使用时都必须赋值,否则会被视为不存在的变量。看了上百字,也没明白什么是语句,小萌暗自抱怨到。于是她随手在交互模式下输入如下: advice=''boss,we want have a lunch''刚输完一句,小萌就停下了,等等,刚才输入的不就是语句吗?前面都做过不少示例了,看看还用过什么语句。在交互模式下写的第一个程序不就是print语句吗?对了,还可以知道这个语句中advice的类型是什么样的。小萌想完,在交互模式下输入如下: typeadvice这个语句中advice的类型是字符串(str)类型。还有什么类型的赋值语句呢?对了,前面还学习了整型和浮点型,交互模式输入: money=99999999 typemoney spend=1.11111111 typespend不错,把之前学习的温习了一下。于是小萌又在交互模式下输入如下: so happySyntaxError: invalid syntax哎呀,怎么又犯糊涂了,变量是一定要赋值的。于是重新输入: print''so happy,it is a perfect forenoon''so happy,it is a perfect forenoon小萌突然感觉有人站在自己旁边,原来是小智。小智盯着交互模式输入界面,突然说道:这个用状态图展示会更直观。说完就帮小萌画出了一个变量状态图,如图2-6所示。一般情况下,我们用状态图表示变量的状态。左边是变量名称,右边是变量值。状态图显示了赋值语句的最终操作结果。图2-6 变量的状态图原来小智知道这么多,小萌心中暗暗高兴起来,感谢小智的指导。这真是一个愉快的上午,不过真的很饿。好像到服务区了。boss宣布在这里休息一下,顺便补充点能量再往前走。2.5 表 达 式表达式是值、变量和操作符的组合。单独一个值可以看作表达式,单独的变量也可以看作表达式。这个怎么理解呢?小萌不解地看着这么短短一条语句,脑袋怎么都拐不过弯。今天的午餐真不错,原来是小智,小萌突然来了精神,可以向小智请教请教啊。小智,中午好,能帮我看看这个怎么理解吗?小智过去看了看,思考了一下,给小萌做了如下讲解。表达式和语句一般不容易区分,很多人会将两者混在一起。那么语句和表达式之间有什么区别呢?表达式是某事,语句就是做某事,也就是告诉计算机做什么。比如3*3是9,print3*3输出也是9。区别在哪里呢?我们在交互模式下输入如下: 3*39 print3*39在交互模式下,结果都是一样的。这是因为解释器总是输出所有表达式的值(内部都使用相同的函数对结果进行呈现,后面会有详细介绍)。一般情况下,Python不会这么做,毕竟3*3这样的表达式不能做什么有趣的事情,而编写print3*3会有一个显式的输出结果9。语句和表达式之间的区别在赋值时表现得更加明显。因为语句不是表达式,所以没有值可供交互式解释器输出。比如在交互模式下输入如下: a=100 10*10100从输入结果可以看到,赋值语句输入完成后,下面立刻出现了新的提示输入符。表达式输入完成后,下面立刻得到了结果。不过对于赋值语句,有些东西已经变了,变量a现在绑定了一个值100。这个是语句特性的一般定义:它们改变了事物。比如,赋值语句改变了变量,print语句改变了屏幕显示的内容。赋值语句可能是所有计算机程序设计语言中最重要的语句类型,尽管现在还难以说清赋值语句的重要性。变量就像临时的存储器(就像厨房中的锅碗瓢盆一样),强大之处在于,在操作变量时并不需要知道存储了什么值。比如,即使不知道x和y的值到底是多少,也会知道x*y的结果就是x和y的乘积。所以,可以通过多种方法使用变量,而不需要知道在程序运行时,最终存储的值是什么。2.6 运算符和操作对象运算符和操作对象是计算机中比较常见的,所有计算都涉及运算符和操作对象。本节将介绍Python中的运算符和操作对象。2.6.1 什么是运算符和操作对象运算符是一些特殊符号的集合,我们前面学习的加( )、减(-)、乘(*)、除()、地板除()、取余(%)等都是运算符。操作对象是由运算符连接起来的对象。加、减、乘、除4种运算符是我们从小学就开始接触的,不过乘除的写法不一样,这个要记住。小萌看到这个标题后快速回忆,这个学习方式不错,不但能温故知新,还做了联想记忆和纵向比对,值得借鉴。Python支持以下8种运算符:(1)算术运算符。(2)比较(关系)运算符。(3)赋值运算符。(4)逻辑运算符。(5)位运算符。(6)成员运算符。(7)身份运算符。(8)运算符优先级。这么多,看来今天可以大开眼界了,小萌心里一阵狂喜。接下来让我们一个一个学习吧!2.6.2 算术运算符表2-1为算术运算符的描述和实例。假设变量a为10,变量b为5。表2-1 算术运算符运算符 描述 实例加:两个对象相加 a b 输出结果为15- 减:得到负数或一个数减去另一个数 a - b 输出结果为5* 乘:两个数相乘或返回一个被重复若干次的字符串 a * b 输出结果为50 除:x除以y a b 输出结果为2% 取模:返回除法的余数 b % a 输出结果为0** 幂:返回x的y次幂 a**b为10的5次方,输出结果为100000 取整除(地板除):返回商的整数部分 92输出结果为4,9.02.0输出结果为4.0下面进行实战。在交互模式输入如下: a=10 b=5 printa b15 printa-b5 printa*b50 printab2.0 printa**b100000 print924 print9.02.04.0此处的加、减、乘、除,取余、取整除前面都已经做过详细介绍,较好理解。但是幂运算与数学中学习的乘方运算的形式是不一样的,是a2这样的形式。有没有更好的方式让人更容易记住这个符号呢?有一个很好的例子,为什么会出现32位和64位操作系统,并且现在大家都趋向于安装64位的软件。先看交互模式下的两个输入: 2**321024102410244.0 2**6410241024102417179869184.0第一个输入,2**32是2的32次方,这是32位操作系统最大支持内存的字节数,除以第一个1024是转换为KB,1KB=1024B,除以第二个1024是转换为MB,1MB=1024KB,除以第三个1024是转换为GB,1GB=1024MB。这个结果意味着32位操作系统最大只能支持4GB的内存,现在手机都是4GB标配了,电脑4GB怎么够呢?所以大家都趋向于选择64位操作系统了,64位操作系统能支持多大内存,大家可以自行计算一下。2.6.3 比较运算符表2-2为比较运算符的描述和实例。以下假设变量a为10、变量b为20。表2-2 比较运算符运算符 描述 实例== 等于:比较对象是否相等 a == b 返回False。!= 不等于:比较两个对象是否不相等 a != b 返回True. 大于:返回x是否大于y a b 返回False。= 大于等于:返回x是否大于等于y。 a = b 返回False。 a=10 b=20 a==bFalse a!=bTrue abFalse a a=bFalse a a 10=bTrue a 10bFalse a a a==b-10True小智:小萌,注意到比较运算的特色了吗?小萌:比较运算只返回True和False两个值。小智:对的,能看出比较运算符两边的值和比较的结果有什么特色吗,特别是对于==、、=这5个比较运算符的结果?小萌:让我仔细观察观察,对于这些比较运算,只要左边和右边的操作数满足操作符的条件,结果就是True,不满足就是False。小智:你理解的没错,其实可以通俗地理解为,比较结构符合大家的心里预期,结果就是True,不符合结果就是False。比如上面的例子中a符号。和!=一样,也表示不等于,在Python 3中已去除该符号。若以后看到运算符,则使用的是Python 2。2.6.4 赋值运算符表2-3为赋值运算符的描述和实例。假设变量a为10,变量b为20。表2-3 赋值运算符运算符 描述 实例= 简单的赋值运算符 c = a b,将a b的运算结果赋值给c = 加法赋值运算符 c= a,等效于c = c a-= 减法赋值运算符 c -= a,等效于 c = c - a*= 乘法赋值运算符 c *= a,等效于 c = c * a= 除法赋值运算符 c = a,等效于 c = c a%= 取模赋值运算符 c %= a,等效于 c = c % a**= 幂赋值运算符 c **= a,等效于 c = c ** a= 取整(地板)除赋值运算符 c = a,等效于 c = c a下面进行实战。 a=10 b=20 c=0 c=a b printc30 c =10 printc40 c-=a printc30 c*=a printc300 c=a printc30.0 c%=a printc0.0 c=a**5 printc100000 c=b printb20 printc50002.6.5 位运算符位运算符是把数字看作二进制进行计算的。表2-4为Python中位运算的描述和实例。假设变量a为60,变量b为13。表2-4 位运算符运算符 描述 实例& 按位与运算符:若参与运算的两个值的两个相应位都为1,则该位的结果为1;否则为0 a & b 输出结果 12,二进制解释:0000 1100| 按位或运算符:只要对应的两个二进制位有一个为1,结果位就为1 a | b 输出结果 61,二进制解释:0011 1101^ 按位异或运算符:当两个对应的二进制位相异时,结果为1 a ^ b 输出结果 49,二进制解释:0011 0001~ 按位取反运算符:对数据的每个二进制位取反,即把1变为0,把0变为1 ~a 输出结果 -61,二进制解释:1100 0011,在一个有符号二进制数的补码形式 右移动运算符:把左边运算数的各个二进制位全部右移若干位,右边的运算数指定移动的位数 a 2 输出结果 15,二进制解释:0000 1111下面进行实战。 a=60 b=13 c=0 c=a&b printc12 c=a|b printc61 c=a^b printc49 c=~a printc-61 c=a printc240 c=a2 printc152.6.6 逻辑运算符Python语言支持逻辑运算符。表2-5为逻辑运算符的描述和实例,假设变量a为10,变量b为20。表2-5 逻辑运算符运算符 逻辑表达式 描述 实例and x and y 布尔与:如果 x 为 False,x and y就返回False;否则返回 y 的计算值 a and b 返回20or x or y 布尔或:如果 x 是非 0,就返回 x 的值;否则返回 y 的计算值 a or b 返回10not not x 布尔非:如果 x 为 True,就返回 False;如果 x 为 False,就返回 True nota and b返回False下面进行实战。 a=10 b=20 a and b20 a or b10 not aFalse not bFalse not -1False not FalseTrue not TrueFalse2.6.7 成员运算符除了之前介绍的运算符外,Python还支持成员运算符,表2-6为成员运算符的描述和实例。表2-6 成员运算符运算符 描述 实例in 如果在指定的序列中找到值,就返回 True;否则返回 False x在y序列中,如果x在y序列中,就返回Truenot in 如果在指定的序列中没有找到值,就返回True;否则返回 False x不在y序列中,如果x不在y序列中,就返回True下面进行实战。 a=10 b=5 list=[1,2,3,4,5] printa in listFalse printa not in listTrue printb in listTrue printb not in listFalse你可能会疑惑list是什么,list是一个集合,此处不做具体讲解,后面章节会有详细介绍。2.6.8 身份运算符身份运算符用于比较两个对象的存储单元,表2-7为身份运算符的描述和实例。表2-7 身份运算符运算符 描述 实例is is判断两个标识符是否引用自一个对象 x is y,如果idx等于idy,is返回结果1is not is not用于判断两个标识符是否引用自不同对象 x is not y,如果idx不等于idy,is not就返回结果1下面进行实战。 a=10 b=10 printa is bTrue printa is not bFalse b=20 printa is bFalse printa is not bTrue后面已对变量b重新赋值,因而输出结果与前面不太一致。2.6.9 运算符优先级表2-8列出了从最高到最低优先级的所有运算符。表2-8 运算符优先级运算符 描述** 指数(最高优先级)~ - 按位翻转,一元加号和减号(最后两个的方法名为@ 和 -@)* % 乘、除、取模和取整除- 加法、减法 = 比较运算符(续表)运算符 描述 == != 等于运算符= %= = = -== *= **= 赋值运算符is is not 身份运算符in not in 成员运算符not or and 逻辑运算符当一个表达式中出现多个操作符时,求值的顺序依赖于优先级规则。Python遵守数学操作符的传统规则。小萌,还记得数学里面操作符的传统规则是怎样的吗?恩,有括号先算括号里的,无论是括号里还是括号外的,都是先乘除、后加减。在Python中有更多操作运算符,可以使用缩略词PEMDAS帮助记忆部分规则。(1)括号(Parentheses,P)拥有最高优先级,可以强制表达式按照需要的顺序求值,括号中的表达式会优先执行,也可以利用括号使得表达式更加易读。例如,对于一个表达式,想要执行完加减后再做乘除运算,交互模式输入如下: a=20 b=15 c=10 d=5 e=0 e=a-b*cd print''a-b*cd='',ea-b*cd= 10.0顺利达到了我们想要的结果,如果不加括号会怎样呢? e=a-b*cd print''a-b*cd='',ea-b*cd= -10.0结果与前面完全不同了,这里根据先乘除后加减进行运算。如果表达式比较长,加上括号就可以使得表达式更易读。 e=a b c-c*d print''a b c-c*d='',ea b c-c*d= -5以上输入没有加括号,表达式本身没有问题,但看起来不太直观。如果进行如下输入: e=a b c-c*d print''a b c-c*d='',ea b c-c*d= -5这样看起来就非常直观。运算结果还是一样的,但我们一看就能明白该表达式的执行顺序是怎样的。(2)乘方(Exponentiation,E)操作拥有次高的优先级,例如: 2**1 24 2**1 28 2**2*312 2*2**316 2**2*364以上结果解释:2的一次方为1,加2后结果为4;1加2等于3,2的3次方结果为8;2的2次方为4,4乘以3等于12;2的3次方为8,2乘以8等于16;2乘以3等于6,2的6次方为64。(3)乘法(Multiplication,M)和除法(Division,D)优先级相同,并且高于有相同优先级的加法(Addition,A)和减法(Subtraction,S),例如: a b*c-d165 a*bc d35.0(4)优先级相同的操作按照自左向右的顺序求值(除了乘方外),例如: a b-c d30 a b-c-d20其他运算符的优先级在实际使用时可以自行尝试判断。若通过观察判断不了,则可以在交互模式下通过实验进行判断。2.7 字符串操作字符串是 Python 中最常用的数据类型。我们可以使用引号(''或")创建字符串。通常字符串不能进行数学操作,即使看起来像数字也不行。字符串不能进行除法、减法和字符串之间的乘法运算。下面的操作都是非法的。 ''hello''3Traceback most recent call last: File "", line 1, in ''hello''3TypeError: unsupported operand types for : ''str'' and ''int'' ''world''-1Traceback most recent call last: File "", line 1, in ''world''-1TypeError: unsupported operand types for -: ''str'' and ''int'' ''hello''*worldTraceback most recent call last: File "", line 1, in ''hello''*worldNameError: name ''world'' is not defined ''hello''-''world''Traceback most recent call last: File "", line 1, in ''hello''-''world''TypeError: unsupported operand types for -: ''str'' and ''str''字符串可以使用操作符 ,但功能和数学中不一样,它会进行拼接(concatenation)操作,即将前后两个字符首尾连接起来。例如: string1=''hello'' string2=''world'' printstring1 string2helloworld输出的字符紧紧挨着,看起来不怎么好看,能不能在两个单词间加一个空格呢?如果想让字符串之间有空格,就可以建一个空字符变量插在相应的字符串之间,让字符串隔开,或者在字符串中加入相应的空格。交互模式下输入如下: string1=''hello'' string2=''world'' space='' '' printstring1 space string2hello world或者 string1=''hello'' string2='' world'' printstring1 string2hello world这些是字符串的一些简单操作,在后续章节中会介绍更多、更实用的字符串操作。小萌,你有没有发现进行了这么多操作,操作中都没有出现中文,这是怎么回事呢?是啊,虽说一直用英文操作,在编码时可以学习英文,但很多时候我还是喜欢用中文表达。我们目前没有操作中文,是因为Python不支持中文吗?Python是支持中文的。正如我们前面所说,字符串也是一种数据类型,但是字符串特殊的是有编码问题。因为计算机只能处理数字,其实只认识0和1,即二进制。如果要处理文本,就必须先把文本转换为数字才能处理。最早的计算机在设计时采用8比特(bit)为一个字节(byte),所以一个字节(8位)能表示的最大整数是255(二进制11111111等于十进制255,简单表示为2**8-1=255)。如果要表示更大的整数,就必须用更多字节。比如两个字节(16位)可以表示的最大整数是65535(2**16-1),4个字节(32位)可以表示的最大整数是4294967295(2**32-1)。由于计算机是美国人发明的,因此最早只有127个字母被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码。例如,大写字母A的编码是65,小写字母z的编码是122。要处理中文,显然一个字节是不够的,至少需要两个字节,而且不能和ASCII编码冲突,所以中国制定了GB2312编码,用来把中文编进去。可以想象,全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准,就不可避免地出现冲突。结果就是,在多语言混合的文本中就会显示乱码。当时各国对编码问题的感觉如图2-7所示。图2-7 令人头疼的字符编码Unicode应运而生。Unicode把所有语言都统一到一套编码里,这样就不会有乱码问题了。Unicode标准在不断发展,最常用的是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。下面我们来看ASCII编码和Unicode编码的区别:ASCII编码是1个字节,而Unicode编码通常是两个字节。字母A用ASCII编码是十进制的65,二进制的01000001。字符0用ASCII编码是十进制的48,二进制的00110000。注意字符0和整数0是不同的。汉字中已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。如果把ASCII编码的A用Unicode编码,只需要在前面补0就可以,因此A的Unicode编码是00000000 01000001。新的问题又出现了:如果统一成Unicode编码,乱码问题从此消失了。但是写的文本基本上全部是英文时,用Unicode编码比ASCII编码多一倍存储空间,在存储和传输上十分不划算。本着节约的精神,又出现了把Unicode编码转化为可变长编码的UTF-8编码。UTF-8编码把一个Unicode字符根据不同的数字大小编码成1~6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4~6个字节。如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间,如表2-9所示。表2-9 各种编码方式比较字符 ASCII Unicode UTF-8A 01000001 00000000 01000001 01000001中 x 01001110 00101101 11100100 10111000 10101101从表2-9可以发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上可以看成是UTF-8编码的一部分,所以只支持ASCII编码的大量历史遗留软件可以在UTF-8编码下继续工作。搞清楚ASCII、Unicode和UTF-8的关系后,我们可以总结一下现在计算机系统通用的字符编码工作方式:在计算机内存中,统一使用Unicode编码,当需要保存到硬盘或需要传输时,可以转换为UTF-8编码。例如,用记事本编辑时,从文件读取的UTF-8字符被转换为Unicode字符到内存;编辑完成后,保存时再把Unicode转换为UTF-8保存到文件,如图2-8所示。浏览网页时,服务器会把动态生成的Unicode内容转换为UTF-8再传输到浏览器,如图2-9所示。 图2-8 字符转换 图2-9 服务器、浏览器中的字符串转换我们经常看到很多网页的源码上有类似的信息,表示该网页用的UTF-8编码。在最新的Python 3版本中,字符串是以UTF-8编码的。也就是说,Python 3的字符串支持多语言。比如在交互模式下输入: print''你好,世界!''你好,世界! print''馕齉''馕齉可以看到,在Python 3中,简单和复杂的中文字符都可以正确输出。Python 2中默认的编码格式是ASCII,在没修改编码格式时无法正确输出中文,在读取中文时会报错。Python 2使用中文的语法是在字符串前面加上前缀 u。2.8 注释当程序变得更大、更复杂时,读起来也更困难。程序的各部分之间紧密衔接,想依靠部分代码了解整个程序的功能很困难。在现实中,我们经常很难弄清楚一段代码在做什么、为什么那么做。因此,在程序中加入自然语言的笔记解释程序在做什么是一个不错的主意。这种笔记称为注释(comments),注释必须以#符号开始。注释可以单独占一行,也可以放在语句行的末尾。在交互模式下输入如下: # 打印1 1的结果 print1 12 print1 1 # 打印1 1的结果2从符号#开始到这一行末尾,之间所有内容都被忽略,这部分对程序没有影响。注释信息主要是方便程序员,一个新来的程序员通过注释信息能够更快地了解程序的功能。程序员在经过一段时间后,可能对自己的程序不了解了,利用注释信息能够很快熟悉起来。注释最重要的用途在于解释代码并不显而易见的特性。比如,在以下代码中,注释与代码重复,毫无用处。 r=10#将10赋值给r下面这段代码注释包含代码中隐藏的信息,如果不加注释,就很难让人看懂是什么意思(虽然在实际中可以根据上下文判定,但是需要浪费不必要的思考时间)。 r=10#半径,单位是米选择好的变量名可以减少注释,但长名字会让复杂表达式更难阅读,所以这两者之间需要权衡取舍。2.9 调试这里通过设置的一些错误让读者认识在编写代码过程中的常见问题,以帮助读者熟悉和解决实际遇到的问题。(1)还记得数字类型转换吗?用int转换一个字符,会得到怎样的结果呢?尝试一下,在交互模式下输入: int''hello''Traceback most recent call last: File "", line 1, in int''hello''ValueError: invalid literal for int with base 10: ''hello''开动大脑,思考一下这段语句的功能。(2)在变量和关键字中,若变量被命名为关键字会怎样呢?输入如下: class=''你好''SyntaxError: invalid syntax(3)在算术运算符中,若被除数为0,结果会怎样呢?输入如下: 90Traceback most recent call last: File "", line 1, in 90ZeroDivisionError: division by zero这里的被除数跟数学中的一样,不能为0。2.10 问题解答(1)关键字那么多,我需要全部记住吗?答:可以不用刻意记忆,随着你逐步学习,会碰到一些常用关键字,见多了自然就熟悉了。对于一些不常用的,见到了再回头看是否属于关键字。总之,关键字可以在学习和使用中慢慢记忆。(2)这么多运算符,都需要熟练使用吗?答:能熟练使用当然最好,若不能全部熟练使用,也要有所了解,在实际解决问题时知道应该使用什么运算符。当然,也可以碰到具体问题时再详细研究。(3)字符串的操作只有本章介绍的这些吗?答:字符串还有很多操作,本章介绍的只是一些入门操作,后面的章节会详细介绍。2.11 温故知新,学以致用1. 回顾数据类型相关的概念,如整型、浮点型和数据类型转换。2. 回顾变量和关键字相关的概念,并尝试记住这些关键字。3. 回顾运算符和操作对象,并通过不断调试熟悉各种运算符。4. 回顾字符串的操作及注释,了解编码方式,尝试写注释。5. 小萌和小智约定,明天小智送一颗糖给小萌,并从后天起,小智每天比前一天多送一倍的糖给小萌,到第16天(包含这天),小萌一共可以收到多少颗糖?6. 用4个2与各种运算符进行运算,得到的最大数是多少?7. 结合本章所学,并查阅相关资料,看看下面的代码输出结果是什么,并对结果进行解释。 habit=''Python是一门很有用的编程语言\n我想学好它'' printhabit#你认为的结果是 lenhabit#你认为的结果是