Python中的基本数据类型有数值类型、字符串型、列表、元组、字典、集合等。本章介绍数值类型。数值类型包括整型、布尔型、浮点型和复数类型。
3.1 整型
3.1.1 取值范围
和其他语言一样,Python中也有整型数据类型,与很多语言不同的是,Python的最新版本中( Python 3.6.1)的整型可表示的数值范围非常大,其大小只受计算机内存大小的限制。例如,下面这个非常大的整数的整型运算,假如在C语言中,需要自行构造数据结构实现,而在Python 3.6.1中,使用语言内置的整型即可实现,而且其算法经过了优化,计算速度相当快:
>>>> 1111**1000 # 1111的1000次方
>>>> 51767708443443359586930107440187874650172620553914244948309038572762729578198094152582315911797136186237712453.....
3.1.2 支持的运算
我们前面讲过,当我们谈论某个数据类型时,一方面规定了该数据类型的取值及其范围,另一方面规定了在该数据类型上可以执行的运算。对于整型,可以进行算术运算、位运算和比较运算。
1. 算术运算
整型和浮点型都支持下面几种算术运算:
运算 | 结果 | 注释 |
---|---|---|
x+y | x和y的和(sum of x and y) | |
x-y | x和y的差(difference of x and y) | |
x*y | x和y的积(product of x and y) | |
x/y | x和y的商(quotient of x and y) | (4) |
x//y | x和y的地板商(floor quotient of x and y) | (1) |
x%y | x/y的余数( remainder of x/y) | |
-x | 对x取负数(x negated) | |
+x | x本身(x unchanged) | |
abs(x) | x的绝对值或称其大小(absolute value or magnitude of x) | |
int(x) | 将x转换为整型 | (3) |
divmod(x,y) | 由(x//y, x%y)组成的数值对 | |
pow(x,y) | x的y次方 | (2) |
x**y | x的y次方 | (2) |
(1) 也称为整数除法,对运算结果进行了取整,不过结果的类型不一定是整型(int),结果总是向负无穷方向取整: 1//2=0, (-1)//2=-1, 1//(-2)=-1,而(-1)//(-2)=0。
(2) 和其他编程语言一样,Python定义pow(0,0)或0**0为1。
(3) int()是一个类型转换函数,把其他类型的值转换成整型值。从浮点型转换为整型将像C语言中那样对浮点数进行舍入(舍去浮点数中的任何小数,而不是四舍五入)
(4) 也称为小数除法,结果是一个"精确的"小数
>>> i=5//3 # i=1
>>> i=5%3 # i=2
>>> i=abs(-1) # i=1
>>> i=int("1234") # i=1234
>>> pair=divmod(5,3) # pair=(1,2)
>>> i=power(2,3) # i=8
>>> i=2**3. # i=8
2. 位运算
整型数支持位运算,就是对整型数对应的二进制数进行按位操作。有以下几种位运算:
操作(运算) | 结果 | 注释 |
---|---|---|
x|y | x和y的按位或 | |
x^y | x和y的按位异或 | |
x&y | x和y的按位与 | |
x<<n | x左移n位 | (1)(2) |
x>>n | x右移n位 | (1)(3) |
~x | x按位取反 |
(1) n为负数时不合法,将会出错
(2) 左移n位等效于乘以pow(2, n)的积,不进行溢出检查
(3) 右移n位等效于除以pow(2,n)的商,不进行溢出检查
# 按位或运算是对整数的对应位进行或运算,得到的结果,
# 位或运算遵循下面的表,参与计算的两位中只要有1个是1,结果就是1
# 1st digit 1 1 0 0
# 2nd digit 1 0 1 0
# result digit 1 1 1 0
>>> i=10 # i的二进制表示(4位即可,高位为0) 1010
>>> j=8 # j的二进制表示(4位即可,高位为0) 1000
>>> k=i|j # k的二进制表示(4位即可,高位为0) 1010,其十进制是10
# 按位与运算是对整数的对应位进行与运算,得到的结果
# 位与运算遵循下面的表,参与计算的两位中都是1,结果才是1
# 1st digit 1 1 0 0
# 2nd digit 1 0 1 0
# result digit 1 0 0 0
>>> i=10 # i的二进制表示(4位即可,高位为0) 1010
>>> j=8 # j的二进制表示(4位即可,高位为0) 1000
>>> k=i&j # k的二进制表示(4位即可,高位为0) 1000,其十进制是8
# 左移位运算就是对将整型数的二进制表示按位左移,
# 右侧补0,相当于乘以2**n
>>> i=10 # i的二进制表示(16位即可,高位为0) 0000 0000 0000 1010
>>> j=i<<2 #j的二进制表示. 0000 0000 0010 1000
>>> j
40
# 右移位运算与左移位相反
>>> i = 10 # 0000 0000 0000 1010
>>> j=i>>2 # 0000 0000 0000 0010 , 十进制为2
# 可以看到起整数除法的效果,即j=i//4
上面的代码中介绍了正整数的右移位运算,负整数的二进制表示和右移位运算该怎么做呢?这涉及到数字表示的原码、反码和补码。先透露一点,计算机中常用补码表示,以解决正负数混合运算的问题。
为了简化问题,我们用四位二进制位表示一个整数。
原码: 一个整数的最基本的二进制表示,在最高位用1表示负号,最高位的0不表示正号,仍表示0。如: 十进制数3的原码是0011, -3的原码是 1011
我们看到,使用原码,对于正整数的相加,能够很好地解决了,但正负数相加就不对了。比如:
3 0 0 1 1 3 0 0 1 1 +3 0 0 1 1 +(-3) 1 0 1 1 ------------ ------------- 6 0 1 1 0 -6 1 1 1 0
因此,引入了反码
反码: 正数的反码就是其原码,负数的反码符号位不变,其余各位按位取反。十进制数3的反码是: 0011, -3的反码是1100。0的反码有两个(±0),分别是0000和1111
3 0 0 1 1 3 1 1 0 0 +3 0 0 1 1 +(-3) 1 1 0 0 ------------- --------------- 6 0 1 1 0 0 1 1 1 1 0的反码
反码中0存在±0两种表示,这不美观,因此,我们引入了补码,解决了上述问题:
补码: 正数的补码是其原码,负数的补码是反码加1,十进制数3的补码是0011,十进制数-3的补码是1101。这时,+0和-0的补码就相同了。
3 0 0 1 1 3 0 0 1 1 +3 0 0 1 1 +(-3) 1 1 0 1 ------------- --------------- 6 0 1 1 0 0 (1)0 0 0 0 (最高位的1由于超出4位而被丢弃)
负整数的右移位运算,是将负整数的低位移出数字,在高位补符号位,即1
>>> i=-3 # 补码是 1 1 0 1
>>> j=i>>2 # j的补码 1 1 1 1
>>> j
-1
j的补码是1111,由它不能直观地看出其十进制值来,需要将其转换为原码表示,求补码的补码,即得到原码,还记得补码怎么求吗: 先求反码,再加上1就可以了,下面先求上面这个补码的反码:
补码看做原码 1 1 1 1 符号位是1,表示一个负数 补码的反码 1 0 0 0 则反码的符号位不变,其余位求反 补码的补码 1 0 0 1 这就是求出来的原码,还记得吗 符号位的1表示符号,最低位的1表示绝对值是1 这个原码的十进制表示是 -1
# 负数的按位求反运算也很容易了
>>> i = -3 # 补码是1 1 0 1
>>> j=~i. # 补码是0 0 1 0 补码的补码是(1) 0 0 1 0丢弃最高位
# 十进制值是2
题外话: C语言的带符号整数和无符号整数
不像Python,在一些其他计算机语言中, 内置的整型类型的内存表示是有长度限制的,这个长度限制在制定语言标准时,就规定好了。例如,C语言从创生起到现在为止,经过了3个主要的国际标准阶段,第一阶段简称C89标准,在1989年完成标准化;第二个阶段简称C99标准,在1999年完成标准化;第三个阶段简称C11标准,在2011年完成标准化。严格符合C语言标准的C语言实现(指C语言的编程环境和编译器等)称为标准C语言。MS C(微软的C实现)和GCC(Linux的C实现)是两个主要的C语言实现,它们除了支持标准C语言的功能之外,还对C语言实现了扩展。当编写可移植的C程序时,可移植程度越高,越应当增加使用符合标准C语言的代码。所有C语言实现,在编译C程序时,都提供开关选项,用以在编译标准C语言程序、支持的标准C版本以及扩展C程序之间进行选择。随着C标准的演进,标准C语言支持越来越丰富的类型定义、内置功能和标准库函数。
在C语言中,整型分为短整型、整型和长整型三种,每种整型又有带符号和无符号两种类型,因此整型有short int、unsigned short int;int、 unsigned int;long int、unsigned long int六种类型,对于这6种整型,C标准支持的最小取值范围又是不同的。为什么说C标准支持的最小取值范围呢? 因为C标准规定了这6种整型的二进制表示的最小位数,C实现可以对此进行扩展,但一般不超过C编程环境所在的设备硬件和操作系统所能支持的自然长度。比如在32位计算机和32位操作系统上,int类型最大可用32位二进制数表示。但C89规定,int类型的二进制表示最小为16位,也就是说,从移植性角度来说,凡是符合C89的C语言编程环境都可安全地假设它支持16位int型整数。
所谓带符号和无符号整型,其区别是带符号整型的二进制表示中可以带一个符号位,用于表示负数符号,因此带符号整型可以表示正、负整数,多数计算设备中,带符号整型的二进制表示是用补码表示的;而无符号整型则不带符号位,它的所有二进制位都用于表示数字本身,因此无符号整型只能表示正整数,它用原码(对于正整数来说,反码和补码都是原码)表示即可。
比如,对于用16位二进制数表示的x86系列计算机中,某个用补码表示int型的C实现下:
int的最大值为: 十进制: 32767 二进制: 0111 1111 1111 1111
int的最小值为: 绝对值 -32768
它的补码的计算法:
首先,符号位置1
二进制原码 1000 0000 0000 0000 (符号位和最高位占用了同一位),注意: 1111 1111 1111 1111 是-32767
取反码: 0111 1111 1111 1111
补码=反码+1: 1000 0000 0000 0000
那么,下面的代码的结果是什么呢:
int i=32767; printf("%d\n", i+1)
从上面的讨论中,我们知道: 32767的二进制表示是0111 1111 1111 1111,由于计算机中的运算都是以二进制方式进行的,对它加1,得到的二进制表示是1000 0000 0000 0000,我们看到,它变成了-32768的补码形式,因此,上面的程序得到的结果是:
-32768
那么,下面这个程序的结果是什么呢?
int i=-32768; printf("%d\n", i-1);
你总结出规律来了吗?
C99和C11只是对这些整型所规定的可接受的二进制表示的最小长度有所不同,即这些整型的取值范围有所不同,但原理是一样的。
对于用16位二进制表示的unsigned int类型,其
最小值为 0, 二进制为:0000 0000 0000 0000
最大值为65535, 二进制为: 1111 1111 1111 1111
3. 比较运算
运算(操作) | 含义 |
---|---|
< |
小于 |
<= |
小于等于 |
> |
大于 |
>= |
大于等于 |
== |
相等 |
!= |
不等于 |
is |
是相同的对象(1) |
is not |
是不同的对象(1) |
(1)is和is not判断两个对象是否是同一个对象。对于值相同的整型对象,Python将不同变量名指向了同一个值(内存块),所以使用is/is not判断分别相当于==/!=判断。
>>> i=3
>>> j=3
>>> i is j
True
>>> id(i) # i和j是同一个对象,所以i is j == True
10914432
>>> id(j)
10914432
>>> j=4
>>> i is j
False
>>> id(j) # j和i不是同一个对象,所以i is j == False
10917536
3.2 布尔类型
布尔类型是一种特殊的整型。
3.2.1 取值范围
布尔类型只有True/False两个值。
3.2.2 条件判断
if 语句和while语句对条件进行判断,最简单的条件判断如下所示:
if True: print("True")
else: print("False")
while(True):
print("I am always True")
3.2.3 布尔表达式
在能够使用布尔变量的地方,都可以使用布尔表达式代替,布尔表达式是对布尔值进行布尔运算得到的。常见的布尔运算如下所示:
# 布尔或运算
>>> cond1 or cond2 # or是运算符,cond1和cond2是布尔值
# 布尔与运算
>>> cond1 and cond2 # and是运算符,cond1和cond2是布尔值
# 布尔非运算
>>> not cond # not是运算符,cond是布尔值
布尔运算的结果由一个叫做真值表的东东决定:
布尔或运算
cond1 | cond2 | 结果 |
---|---|---|
True | True | True |
True | False | True |
False | True | True |
False | False | False |
布尔与运算
cond1 | cond2 | 结果 |
---|---|---|
True | True | True |
True | False | False |
False | True | False |
False | False | False |
布尔非运算
cond | 结果 |
---|---|
True | False |
False | True |
3.2.3 类型转换
在条件判断和布尔表达式中出现其他类型时,会将其他类型转换成布尔类型使用。其他类型的值向布尔类型的常用转换规则如下:
- 0或None或空字符(串)转换成False
- 非0值、非None或非空字符(串)转换成True
布尔类型有时会转换成整型使用,其转换规则很简单:
- True转换成1
- False转换成0
>>> bool(1)
True
>>> bool(0)
False
>>> bool(None)
False
>>> bool("")
False
>>> bool(1.2)
True
>>> bool(-0.1)
True
>>> bool(0.0)
False
>>> bool("abc")
True
>>> int(True)
1
>>> int(False)
0
下面是字符串类型向布尔类型转换的一个例子:
>>>def checkCond(cond):
... if cond : print("good luck")
... else: print("bad luck")
>>> cond="something"
>>> checkCond(cond)
good luck
>>> cond=""
>>> checkCond(cond)
bad luck
3.3 浮点类型
3.3.1 取值范围
计算机语言中,使用浮点类型近似表示实数。之所以说是近似表示,是因为由于在内部使用二进制表示我们常用的十进制数,常常存在一些精度的损失。其原因是什么呢? 我们先回顾一下正整数的二进制原码表示,如: 十进制的7,可表示为二进制的111,其转换式如下所示:
7十进制 = 1x22+1x21+1x20=4+2+1
那么,二进制实数: 111.11表示什么呢, 很简单,它表示:
1x22+1x21+1x20+1x2-1+1x2-2=4+2+1+0.5+0.25=7.75
可见,二进制数111.11可以精确的表示十进制实数7.75
但是,十进制数0.3能用二进制精确地表示吗,我们看一下:
2-1=0.5
2-2=0.25
2-3=0.125
2-4 = 0.0625
2-5 =0.03125
2-6=0.015625
我们看到,如果把用二进制小数表示十进制小数看成一个拼图游戏,要表示的十进制数看成最终的结果,用于拼凑这个结果的小积木块就是2-n(n=1, 2, ...),其中最大的小积木块的值是1/2,然后依次是1/4,1/8,1/16,每一级积木块的体积都是上一级的二分之一,由于计算机中浮点数的表示是有长度限制的,这些小积木块的数量是有限的,且由于存在小积木块之间特殊的体积关系,所以使用二进制数并不能精确地表示全部的十进制数,除非这些小积木块能够恰好拼在一起(如下面的0.3125就是两个可以恰好拼在一起的小积木块)。譬如,十进制数0.3可以使用二进制数0.0101近似的表示,其表示的精确的十进制数是
0.25+0.0625=0.3125
你也可以用更小的积木块更近似的表示其中的0.0625,达到更近似表示0.3的目的,但是你始终无法准确地表示它。对比整数的表示,在整数中,最小的积木块是0或1,所以只要内存足够大,Python可以精确表示足够大的整数。
内置的浮点型数据不能精确地表示全部十进制数,是目前的计算机编程语言普遍存在的一个局限。但对于要完成的大多数任务,内置的精度是足够的。对于C语言和C++语言,内置有float和double两种浮点类型,分别称为单精度和双精度浮点类型,它们在计算机中所占的内存单元大小和二进制表示的结构有所不同,因此,后者的精度是前者的两倍,可表示的实数范围也更大。单精度浮点型的精度(有效小数位数)为7位,比如一个单精度浮点数0.12345678901,7以后的数字就是不精确的了。
与C不一样,Python 3.6中只有双精度浮点数,而没有单精度浮点数。
3.3.2 浮点数支持的运算
1. 算术运算
运算 | 结果 | 注释 |
---|---|---|
x+y | x和y的和(sum of x and y) | |
x-y | x和y的差(difference of x and y) | |
x*y | x和y的积(product of x and y) | |
x/y | x和y的商(quotient of x and y) | (4) |
x//y | x和y的地板商(floor quotient of x and y) | (1) |
x%y | x/y的余数( remainder of x/y) | |
-x | 对x取负数(x negated) | |
+x | x本身(x unchanged) | |
abs(x) | x的绝对值或称其大小(absolute value or magnitude of x) | |
int(x) | 将x转换为整型 | (3) |
divmod(x,y) | 由(x//y, x%y)组成的数值对 | |
pow(x,y) | x的y次方 | (2) |
x**y | x的y次方 | (2) |
(1) 也称为整数除法,对运算结果进行了取整,不过结果的类型不一定是整型(int),结果总是向负无穷方向取整: 1//2=0, (-1)//2=-1, 1//(-2)=-1,而(-1)//(-2)=0。
(2) 和其他编程语言一样,Python定义pow(0,0)或0**0为1。
(3) int()是一个类型转换函数,把其他类型的值转换成整型值。从浮点型转换为整型将像C语言中那样对浮点数进行舍入(舍去浮点数中的任何小数,而不是四舍五入)
(4) 也称为小数除法,结果是一个"精确的"小数
浮点数还支持下面的算术运算:
运算 | 结果 |
---|---|
math.trunc(x) | 将x截断为整型数 |
round(x[, n]) | x舍入为n位有效数字,四舍五入为偶数。假如省略了n,它默认为0 |
math.floor(x) | 小于等于x的最大整数 |
math.ceil(x) | 大于等于x的最小整数 |
还有更多数值运算,参见math和cmath模块的说明。
2. 比较运算
运算 | 含义 |
---|---|
< | 小于 |
<= | 小于等于 |
> | 大于 |
>= | 大于等于 |
== | 等于 |
!= | 不等于 |
is | 是相同的对象 |
is not | 不是相同的对象 |
☺: 上文讲整型,与现在讲浮点数时,谈到它们受到支持的运算是大部分相同的,但它们并不是只支持整型之间或只支持浮点数之间的运算,而是可以支持不同类型的数据之间的运算的。这时,涉及到类型转换的问题,也就是,参与运算的数据,要从等级较低的类型转换为等级较高的类型,如: 对于整型和浮点型的混合运算,整型数先要转换成浮点数,例子代码如下:
# cal1.py i=3 f=4.2 r=i+f # 等价于 r=float(i)+f print(r)
运行结果是
$ ./cal1.py $ 7.2
到目前为止我们学过的内置数据类型: 布尔型、整型和浮点型,参与混合运算时的等级从低到高依次是: 布尔型、整型、浮点型。举个例子:
# cal2.py b=True i=3 f=4.2 r=b+i+f # 等价于r=float(b)+float(i)+f print(r)
结果是:
$ ./cal2.py $ 8.2