教孩子学Python编程
上QQ阅读APP看书,第一时间看更新

2.2 各种小符号——Python中的基本运算符

在第1章中,我们已经认识了Python中的四则运算符号,其实Python中还提供了许多高级的运算符,概括为如下几类。

• 算数运算符

• 位运算符

• 比较运算符

• 成员运算符

• 赋值运算符

• 身份运算符

• 逻辑运算符

• 符号运算符

看到Python中的这八大类运算符,你是不是略微有一些吃惊,不要被它们看上去“高大上”的名字吓到,它们的使命都是帮助我们进行某种运算或操作的,学会使用它们并不难。下面我们一起关注一下Python中的这些有趣的小符号。

2.2.1 算数运算符

算数运算符

算数运算符用来进行简单的数学运算。第1章中,我们简单地使用了四则运算符,这里复习一下。打开IDLE集成环境,新建一个Python文件,将其命名为operation.py,在其中编写如下代码:

在上面的运算中,最后一次除法运算有些奇怪,你一定想问1除以2的结果不应该是0.5吗,为什么Python计算出了0,这也错得太离谱了。其实这是有原因的,我们知道在计算机中数值的存储分为整数和浮点数,在Python中,当整数与整数进行运算时,计算的结果只允许为整数,所有小数部分都要被省略掉,因此上面的除法算出了结果0;如果进行运算的两个数任意一个为浮点数,则计算的结果为浮点数。例如,可以将代码修改如下来得到精确的除法运算结果:

帮你解惑

float()是Python中内置的函数,其作用是将整数或字符串转换成浮点数。

除了四则运算符外,还有3个十分重要的算数运算符,它们分别是取模运算符“%”、取整运算符“//”和幂运算符“**”。

取模运算也叫取余运算。其作用是计算除法运算后的余数,例如下面的代码:

取整运算与取模运算相对应,其作用是取除法运算的整数部分。需要注意,如果在整型数之间进行取整运算,其结果和直接进行除法运算一致。因此,取整运算通常用在浮点数之间,代码如下:

幂运算符用来进行数学上的幂运算。幂运算是一种比乘法运算更复杂的数学运算。幂运算其实也是乘法运算,其实质是乘法运算的一种简写。例如,你可以用下面的表达式来计算6个7相乘的运算:

使用幂运算符就简单许多。下面的表达式也是计算6个7相乘:

我们也可以将上面的幂运算读作7的6次方。

帮你解惑

对于幂运算符左右两边的操作数,在数学上,左边的叫作底数,右边的叫作指数。你可能听过一个概念“指数爆炸”,在幂运算中,指数略微增大都会极大地增大最终的运算结果。

2.2.2 比较运算符

比较运算符

比较运算符的作用是对两个操作数进行比较。就像在生活中,父母总是会拿你的成绩与“别人家的孩子”进行比较,然后得出你没有别人努力的结论。在Python中常用的比较运算符有7种:

• 等于比较“==”

• 不等于比较“!=”

• 不等于比较“<>”

• 大于比较“>”

• 小于比较“<”

• 大于等于比较“>=”

• 小于等于比较“<=”

首先,对于所有的比较运算符,其运算结果都将返回一个布尔值。关于布尔类型的更多内容,后面会专门介绍。这里只需要明白,布尔类型只有两种值,一种是True;另一种是False。True表示真,对于比较运算,则表示此次比较是成立的;False表示假,对于比较运算,则表示此次比较是不成立的。示例代码如下:

帮你解惑

在Python 2.x版本中,“!=”和“<>”都是不等于运算符。其作用完全一样,在Python 3.x中只能使用“!=”进行不等运算。

Python中的字符串也可以进行比较,对于字符串类型的操作数在进行比较时,会将字符串拆成逐个字符进行比较,每一个字符实际上都对应一个字符码。你也可以简单地理解,我们所使用的字符排列在一个有序的字符表中,靠前的字符码小,靠后的字符码大。例如,字符“a”的字符码小于字符“z”的字符码:

如果是字符串的比较,就会从左向右依次对字符进行比较,如果当前字符比较结果为相等,就会取下一个字符进行比较,直到比较出不相等的字符,将比较结果返回,如果比较完所有字符都相等,则字符串的比较结果为相等。示例代码如下:

帮你解惑

其实,在Python中任何值都可以进行比较,Python是一门完全面向对象的语言,随着学习的深入我们会慢慢理解和深入面向对象的内容。但是需要注意,不同类型的值之间进行比较往往是无意义的,就好像你用小明的体重与小王的身高进行比较,在数学上虽然可以进行,但是这种比较没有任何实际的意义。

2.2.3 赋值运算符

赋值运算符

我们前面讲过,在Python中,变量好似门牌号,对变量进行赋值就好比将变量这个门牌号贴在对应的门户上。我们前面一直使用的“=”就是基础的赋值运算符,也叫作简单赋值运算符。我们前面学习了7种算术运算符,每个算术运算符又可以和简单赋值运算符进行复合变成复合赋值运算符。复合赋值运算符有如下7种:

• 加法赋值运算符“+=”

• 取模赋值运算符“%=”

• 减法赋值运算符“-=”

• 幂赋值运算符“**=”

• 乘法赋值运算符“*=”

• 取整赋值运算符“//=”

• 除法赋值运算符“/=”

这些复合赋值运算符十分容易理解,其作用是将当前变量指向的值与运算数进行相应的算数运算后,再将结果赋值给当前变量,示例代码如下:

帮你解惑

如果你是编程初学者,请额外注意,“==”才是等于运算符,“=”是赋值运算符,千万不要混淆这两个运算符的用法。

2.2.4 逻辑运算符

逻辑运算符

布尔值也称为逻辑值,Python中的逻辑运算符有3种:

• 逻辑与运算“and”

• 逻辑或运算“or”

• 逻辑非运算“not”

逻辑运算符的作用是对布尔值之间进行运算。对于逻辑与运算,左右两边的操作数都为布尔值真时,运算的结果才为真,两个操作数中有一个操作数为假时,运算结果则为假。这就好比一个门上有两把锁,只有两把锁都匹配正确的钥匙才能打开这道门,有一把钥匙不对门就无法打开。

对于逻辑或运算,左右两边的操作数有一个为布尔真时,运算结果则为真,两个操作数都为假时,运算结果才为假。这就好比一个门上有一把锁,我们有两把钥匙,只要有一把钥匙与锁匹配,门就可以打开。

逻辑非运算也可以理解为逻辑取反运算,对真值进行逻辑取反运算将得到结果假,对假值进行逻辑取反运算将得到结果真。

使用代码描述上面的逻辑运算规则如下:

需要注意,逻辑运算符虽然是用来对布尔值进行运算的,但在Python中,对非布尔值进行运算也不会报错。由此可见,Python语言有着十分强的灵活性。对数值进行逻辑运算,数值0会被当作逻辑值假处理,非0数值会被当作逻辑值真处理;对字符串进行逻辑运算,长度为0的字符串会被当作逻辑值假处理,长度非0的字符串会被当作逻辑值真处理,例如:

在实际使用中,我们要尽量减少对非布尔值的逻辑运算,避免出现意料之外的结果。

帮你解惑

在许多编程语言中,逻辑与运算使用符号“&&”,逻辑或运算使用符号“||”,逻辑非运算使用符号“!”。Python则直接使用更易读的“and”“or”“not”。相信这种见文知意的设计方式更易于你学习理解。

2.2.5 位运算符

位运算符

位运算符和计算机存储数据的原理相关。我们知道,在计算机中数据都是以二进制的方式存储的,位运算也是针对二进制数的一种运算。二进制的数值中只有0和1两种数字。Python中支持的位运算符有如下6种:

• 按位与运算符“&”

• 按位或运算符“|”

• 按位异或运算符“^”

• 按位取反运算符“~”

• 按位左移运算符“<<”

• 按位右移运算符“>>”

按位与运算是将两个二进制数的每一位数字进行与运算,若两个数字都为1,则此位的运算结果为1;若两个数字有一个为0,则此位的运算结果为0。示例如下:

按位或运算是将两个二进制数每一位数字进行或运算,若两个数字中有一个数字为1,则此位的运算结果为1;若两个数字都为0,则此位的运算结果为0。示例如下:

按位与运算和或运算与逻辑与运算和或运算有着相似之处,按位异或则是位运算中独特的一种运算符,其将两个二进制数每一位数字进行运算时,若两个数字不相同,则此位的运算结果为1,若两个数字相同,则此位的运算结果为0。示例如下:

按位取反运算符只有一个操作数,其作用是对当前操作数的每一位进行取反运算,若此位数字为1,则运算结果为0;若此位数字为0,则运算结果为1。例如:

运算结果和你想象的很不一样吧?其实对于按位取反运算,我们前面忽略的两个知识点非常重要。

• 知识点1:在Python中,整型数值是由32位或64位二进制位定义的,也就是说,虽然我们定义了二进制数0b1101,实际上这个数值前面的空位都使用0进行了填充,因此在按位取反运算时,前面的空位实际都变成了1。

• 知识点2:Python中所有的数值创建时默认都是有符号的,假设存储一个数值需要32位,我们能够操作的实际上只有31位,左数第一位作为符号位,符号位为0表示这个数值是正数,符号位为1表示这个数值是负数。对于正数,存储在内存中的二进制数很好理解,将此正数的二进制形式放入内存,其余位补零即可。

但是对于负数,其在内存中存储的是二进制补码。计算补码的步骤如下:

(1)确定该数的绝对值的二进制形式。

(2)对此二进制码求反码(按位取反)。

(3)在反码的基础上加1。

根据上面的规则,以十进制数-8为例,其绝对值的二进制形式为0…01000,对其求反码为1…10111,在其基础上再加1得到1…11000,即十进制数-8实际存在内存中的数据如下所示:

额外说一点,计算机中为什么要采用补码的方式来存储数据呢?对于有符号数,左数第一位表示的是符号,那么如果直接进行二进制形式的存储,难免会出现这样一种情况:0可以表示为正数0和负数0,这有悖现实规律。因此,人们采用补码的方式使现实的数值与计算机内存中存储的二进制数据一一对应,正数的补码是其本身,负数的补码是其反码加1。经过这样的计算后,无论是正数0还是负数0,在计算机内存中存储的都是全0码,做到了统一。

理解了计算机中的二进制数存储原理,我们再来看上面的按位取反运算。0b1101按位取反后结果为1……0010,此时左数第一位为1,这个数值已经变成了负数,因此按照负数的存储规则,此时计算机存储的二进制码会被作为补码处理,逆运算其原码,首先减1,结果为1……0001,之后进行按位取反运算,结果为0……1110,其对应的十进制为14,添加负号,最终结果为-14。

按位左移与按位右移运算符直接将二进制数值的每一位数字进行左移或右移,空出的位补0。示例如下:

有一点需要注意,按位左移和按位右移运算符并不会影响符号位。

2.2.6 成员运算符

成员运算符

成员是针对组织或集体而言的,例如你是3年级2班的学生,你就是3年级2班这个班级集体的一个成员。在Python中有许多集合结构,例如字符串就是字符的集合,还有更加复杂的列表、元组等。成员运算符的作用是判断某一个元素是否在某一个集合中。Python中的成员运算符有如下两种:

• 包含运算符"in"

• 非包含运算符"not in"

成员运算符的运算结果将返回布尔值。对于“in”运算符,如果在指定的序列中找到元素,就会返回布尔值True;如果在指定的序列中找不到元素,就会返回布尔值False。示例代码如下:

对于“not in”运算符,其使用的规则与“in”运算符刚好相反,如果在指定的序列中找不到元素,就会返回布尔值True;如果在指定的序列中可以找到元素,就会返回布尔值False。示例代码如下:

帮你解惑

在许多编程语言中都没有成员运算符,开发者想要知道元素是否在某个序列中,通常要用遍历的方式来完成。Python中提供的成员运算符极大地方便了开发者。

2.2.7 身份运算符

身份运算符

每一个成年人都有自己的身份证,无论是考学、购房、购车,还是旅行、乘车、办卡都需要使用身份证。每一个身份证都有独一无二的身份证号,通过身份证号就可以确定某一个具体的人。

我们知道,在Python中所有的数据都是对象,这些对象都存储在某一块内存空间中,通过变量这个门牌号,我们可以获取这些数据对象。其实每一个对象都有自己独一无二的“身份证号”,我们可以使用id()函数来获取它,例如:

身份运算符的作用是判断两个变量所引用的数据对象是不是同一个,也可以理解为比较两个变量指向的数据id值是否相同。

Python中的身份运算符有如下两种:

  • 同身份运算符“is”
  • 非同身份运算符“is not”

对于同身份运算符“is”,当两个变量指向的是同一个对象时,将返回结果True,否则返回结果False。示例代码如下:

需要注意,上面代码中的中括号是Python中的列表对象,后面我们会介绍它;上面代码中的变量a和变量b都指向一个空的列表对象,但是这是两个不同的对象,使用“is”运算符进行运算将得到结果False。

对于非同身份运算符“is not”,其用法和“is”刚好相反,当两个变量所引用的数据对象不是同一个时,运算结果为True,否则为False。示例如下:

帮你解惑

你可能发现了一个有趣的现象,对于两个相同的列表对象,其身份通常是独立的,但是对于数值和字符串类型的对象,相同的值其身份通常也是相同的,例如:

这是由于在Python中,字符串和数值都进行了内存优化,因此对于相同的值,系统并不会开辟新的空间存储。

2.2.8 符号运算符

符号运算符实际上就是我们数学中常用的正号与负号,正号运算符使用符号“+”表示,负号运算符使用符号“-”表示。需要注意,虽然正、负号运算符与加减法运算符的写法一致,意义却不同,并且正负号运算符只有一个操作数,运算符放在操作数的前面,而加减法运算符有两个操作数。

对数值使用正号运算符不会改变数值的符号,即正数依然是正数,负数依然是负数;对数值使用负号运算符会改变数值的符号,即正数会变成负数,负数会变成正数。

2.2.9 运算符的优先级

运算符的优先级决定了运算的顺序。就像四则运算中,我们会先进行加减运算,再进行乘除运算。在Python中,乘除运算符的优先级比加减运算符高。实际上,Python中每一个运算符都有一个明确的优先级。表2-1从高到低列出了运算符的优先级排序。

表2-1 运算符优先级

表2-1列出的运算符优先级表实际上我们不需要特别记忆,将它作为一个工具表,在需要的时候查看即可。更推荐你使用另一种方式来控制Python运算的顺序,那就是使用小括号,小括号是万能的,它可以强制让Python先运算小括号内的表达式,例如:

     a = (1+1)*3
     print a     # 结果为6