2.3 数字
Python 3支持以下3种数字类型。
• 整数(int):也可以称为整型,既可以是正数也可以是负数,但是没有小数部分。Python 2中还存在长整型(long)的数字类型,但Python 3中不存在,因为Python 3中的整数是没有大小限制的,可以把整数当作长整型来使用。
• 浮点数(float):浮点数由整数部分和小数部分组成,浮点数还可以使用科学记数法进行表示,如330可以表示为3.3e2,其含义为3.3e2=3.3×102=330。
• 复数(complex):复数是由实数部分和虚数部分共同组成的数字,在Python中可以使用a+bj表示,其中a和b都是浮点数,a为实数部分,b为虚数部分。例如,1.1+3.2j就是一个复数,其中1.1是实数部分,而3.2是虚数部分。此外,也可以使用complex函数来定义虚数,如1.1+3.2j也可以写作complex(1.1, 3.2)。
在计算机中,数字采用二进制表示,尤其是一些内存地址,使用二进制是比较普遍的,但是采用二进制表示位数会比较多,表达起来不是那么方便,所以为了方便一般会采用八进制或十六进制表示,这些Python也做了良好的支持。此外,Python中还提供了对数字的各种运算,包含算术运算、赋值运算、位运算和比较运算等,Python自身还提供了一些数学函数,如计算绝对值的abs函数、四舍五入的round函数等,这些就是本节将介绍的内容。
2.3.1 算术运算
算术运算主要是指四则运算和一些常见的运算。Python支持多种运算符,假设存在两个数字变量a=3和b=2,算术运算符如表2-1所示。
表2-1 算术运算符
下面使用Python代码来验证表2-1中的结果,并展示一些小数和复数的运算,如代码清单2-6所示。
代码清单2-6:Python中的算术运算
运行上述代码得到的输出结果如下:
可以发现,小数和复数的算术运算在精度上存在一定的误差,对于整数来说并不存在这样的误差,但是对于浮点数来说则存在误差。之所以存在这样的误差,是因为计算机内部采用二进制形式表达浮点数,在运算时会丢失一定的精度,并且在不同的计算机或操作系统中误差的大小也会不同。
2.3.2 比较运算
逻辑学中存在布尔值(bool)。布尔值只有两种取值,要么是真(True)要么是假(False)。比较运算符在数字运算中十分常见,它是一种逻辑运算,会返回布尔值的真或假。只是在Python 3中采用关键字True表示真,而关键字False表示假,它们分别对应数字1和0。
注意 关于Python中的布尔值
Python 3中的布尔值底层也是通过数字表示的,实际上就是用数字1和0分别表示True和False,它们也可以参与数学运算,如可以在Python解释器中进行如下测试:
可以看到,True实际上就是1,所以结果是2。当然,在实际应用中尽量不要将布尔值作为数字参与运算,因为这会大大降低可读性,容易造成误解。
假设存在两个变量a = 5和b = 4,可以通过表2-2对比较运算符进行说明。
表2-2 比较运算符
这些比较运算符还比较简单,下面通过代码进行介绍,如代码清单2-7所示。
代码清单2-7:Python中的比较运算
运行上述代码得到的输出结果如下:
2.3.3 赋值运算
赋值运算是为变量赋值,这也是一种常见的运算。假设存在两个变量a=10和b=3,下面通过表2-3对赋值运算进行说明。
表2-3 赋值运算符
下面通过代码来演示这些赋值运算符的使用,如代码清单2-8所示。
代码清单2-8:Python中的赋值运算
上述内容相对来说比较简单,读者根据表2-3就可以知道其含义,而值得注意的是代码最后的海象运算符,它是Python 3.8版本之后才引入的赋值运算符,允许在表达式中对变量进行赋值。运行上述代码得到的输出结果如下:
初学者可能会将运算符“=”和“==”混淆,下面通过文本框进行解释。
注意 运算符“=”和“==”的区别
“=”是赋值运算符,如a=1,表示将1赋值给变量a;而“==”是一个比较运算符,代表对比两个值是否相等。假设a=1,那么a == 1,结果返回True;a == 2,则结果返回False。
下面解释“:=”被称为海象运算符的原因。
说明 运算符“:=”为什么被称为海象运算符?
运算符“:=”被称为海象运算符是因为这个符号与海象特别像,如下图所示。
海象的眼睛和牙齿与运算符“:=”形似。
2.3.4 位运算
Python中还提供了基于二进制的位运算,这里有必要先介绍Python中二进制的相关知识。这部分内容对于非计算机专业的读者来说或许有一定的难度,所以如果读者只是打算简单使用Python就请忽略本节的内容,因为本节会涉及一些非计算机专业很难理解的概念,如二进制的源码、补码等。
计算机是以二进制记录各类数据的,而Python中提供了不同进制的转换函数。
• bin函数返回二进制数据。
• oct函数返回八进制数据。
• hex函数返回十六进制数据。
也可以通过Python给出的约定直接使用不同进制来表示数字。下面通过代码清单2-9进行介绍。
代码清单2-9:Python中的各类进制
运行上述代码得到的输出结果如下:
可见,可以在Python中轻松完成各种进制的转换。有了这些基础,下面介绍位运算符。假设x = 235(二进制数为0011101011),y = 397(二进制数为0110001101),这里需要注意的是二进制数的第一位在计算内部往往表示正负号,也就是说0表示正数,1表示负数。下面通过表2-4介绍位运算符。
表2-4 位运算符
下面通过代码清单2-10来实现表2-4中的内容。
代码清单2-10:Python中的位运算
运行上述代码得到的输出结果如下:
说明 为什么计算机使用补码表示负数?
这是因为计算机采用二进制表示数据,对于减法来说运算会比较复杂,对于加法来说则比较简单。为了提高性能,计算机采用补码来表示负数,这个时候可以通过加法来实现减法。但是需要注意的是,在补码运算中,符号位也参与运算,如9的源码和-9的补码分别为00001001和11110111。
由此可见,结果等于0,于是计算机就可以把减法变为加法,加快运算。
2.3.5 运算符的优先级
前面介绍了Python的运算符,但是运算符之间是存在优先级的,如简单的四则运算在没有括号的情况下,都是先算乘法和除法,再算加法和减法。下面通过表2-5来说明运算符的优先级,优先级在表格中是从高到低进行展示的。
表2-5 运算符的优先级
下面通过代码清单2-11来学习运算符的优先级。
代码清单2-11:运算符的优先级
需要注意这些算式的优先级注释,运行上述代码得到的输出结果如下:
如果需要改变运算符的优先级,则可以使用圆括号把算式括起来,在圆括号中的是最优先的。有时在一些复杂的算式中也应该加入圆括号,如在代码清单2-11中,存在如下两个容易让人混淆的算式:
这两个算式很容易让初学者或经验不足的开发者认为是按照如下形式计算的:
为了避免这些误解,可以添加圆括号,使代码更具有可读性,降低被误解的可能性,具体如下:
这样就可以明确地表达代码的含义,也不容易误导别人,所以应该多采用这样的代码。
注意 对于复杂的算式的一些建议
对于一些复杂的算式,笔者建议将算式进行分解,化整为零,分步执行,切勿把一个极为复杂的算式写在代码之中,这样很容易产生误解,甚至是错误,复杂的算式可读性不高。
2.3.6 数字计算中常见的函数
在Python中,除了运算符还会提供大量的数学函数。在Python中,函数大体分为三大类:数学函数、三角函数和随机数函数。其中,数学函数和三角函数主要在math模块中,而随机数函数在random模块中,模块的相关内容会在后面讲解,在这里读者只需要知道如何用就可以。
下面先介绍数学函数。在此之前需要先掌握两个常用的数学常量,即圆周率(π)和自然常数(e)。读者对圆周率应该比较熟悉,自然常数在数学中的定义为
其近似值为2.718281828459045。在Python中,e≈math.e,而π≈math.pi,这是数学计算中最常用的两个常量。
说明 在数学中对范围的表示方法
数学中的取数范围是通过区间来表示的,如[1, 10),取值为整数,表示在整数1(含)到10(不含)之间取值,这里的“[”表示包含,而“)”表示不包含。又如(0, 2],取值包括小数和整数,表示取值范围为大于0且小于或等于2的任意实数;(2, 5),取值为整数,表示取大于2且小于5的整数,此时只有3和4这两个整数符合条件。
1. 数学函数
下面通过表2-6来列举一些常见的数学函数。
表2-6 常见的数学函数
下面通过举例进行说明,如代码清单2-12所示。
代码清单2-12:数学函数的使用
运行上述代码得到的输出结果如下:
2. 三角函数
除了表2-6中的数学函数,math模块中还存在三角函数,如表2-7所示。
表2-7 三角函数
下面通过举例进行说明,如代码清单2-13所示。
代码清单2-13:三角函数的使用
运行上述代码得到的输出结果如下:
3. 随机数函数
随机数在日常生活中是十分常用的。例如,在收货时,有时货物很多,很难全覆盖,此时就可能随机从众多货物中抽取一些样本进行检查,用来评估这批货物的质量。Python也对随机数提供了良好的支持,但是需要导入random模块。随机数函数如表2-8所示。
表2-8 随机数函数
下面通过举例进行说明,如代码清单2-14所示。
代码清单2-14:随机数函数的使用
运行上述代码可能得到的输出结果如下:
在代码清单2-14中没有讨论seed函数的使用,也许通过运行代码的结果来解释seed函数的使用会更加直观,所以先编写代码清单2-15。
代码清单2-15:seed函数的使用
需要注意代码中加粗的seed函数,运行上述代码可能得到的输出结果如下:
在无种子的情况下,第一次输出的数字和第二次输出的数字是完全不同的;而在种子为1的情况下,第一次输出的数字和第二次输出的数字是相同的。所以,设置种子可以访问到过去已经产生过的随机数。