前一节中,我们对Python中的对象有了一个基本的了解。从本节开始,我们将分别了解不同类型的对象,并且对不同对象的特点,特别是对象有关的函数进行介绍。
本节将会介绍数字类型,也就是Number类型,在Python中是一种不可变类型。
5.1 整数
Python能够处理任意大小的整数,例如0,-1,9989080等等
除了用一般的十进制表示,也可以用二进制(0x)、八进制(0b)、十六进制表示(0o)。并且不同进制的转换有相应的内置函数:
print("十进制数为:", 5)
print("转换为二进制为:", bin(5))
print("转换为八进制为:", oct(5))
print("转换为十六进制为:", hex(5))
执行结果为:
十进制数为:5
转换为二进制为: 0b101
转换为八进制为: 0o5
转换为十六进制为: 0x5
5.2 浮点数
浮点数也就是小数,例如3.14,5.34,-0.3等等,也可以用科学计数法表示,用e来代替10,例如3.14x109 就要用3.14e9
来表示。
5.3 复数
Python中复数的表示与我们在课本中学到的一致,分为实部和虚部,虚部末尾需要加j或J,例如4.2+5j
,43-0.9J
。需要注意的是,Python中复数的实部和虚部都是浮点数。
复数对象主要的两个属性就是real、imag,分别返回实部和虚部,还有一个conjugate方法,返回共轭复数
>>> num = 4.2+5j
>>>
>>> num.real
4.2
>>> num.imag
5.0
>>> num.conjugate()
(4.2-5j)
5.4 布尔型
在Python中布尔型严格来说也是整数的一种,布尔型只有两个值,True
和False
,主要运用在逻辑运算上。
任意一个类型的对象都可以通过bool()
转换为对应的布尔值,实际程序中可能很少会用到这个函数,但是必须要牢记不同对象对应的布尔值是什么,规则如下:
- 数字(int,float,complex)只要为0,对应布尔值就是False,反之为True
- 空的容器对象(空的str、list、tuple、dict、set)在Python中的布尔值都是 False、非空的为True
- 没有__nonzero__()方法的对象的默认值是True
5.5 运算符与重要的内置函数
了解了各种数字类型之后,接下来就是学习在程序中如何使用它们,这就涉及到各种不同的运算符以及内置函数。
5.5.1 算数运算符
算数运算符包括加减乘除(+,-,*,/,//),取余(%),幂运算(**)。算数运算符的使用有以下几个需要注意的要点
- 操作数类型不一
举个例子,在使用加法的时候,用一个整数加小数是很常见的,譬如9.8+1
,但理论上,Python中使用+
时,左右两个操作数应当是相同类型。之所以9.8+1
这个表达式依然可以正确地执行,是因为Python使用了隐式的数字类型强制转换机制来保证操作数类型一致。
强制转换的机制可以参考《Python核心编程》中的描述:
如果有一个操作数是复数, 另一个操作数被转换为复数。
否则,如果有一个操作数是浮点数, 另一个操作数被转换为浮点数。
否则, 如果有一个操作数是长整数,则另一个操作数被转换为长整数;
否则,两者必然都是普通整数,无须类型转换。
强制转换的规则很好理解,只要记住**精度低的操作数向精度较高的操作数看齐(有时会把精度更高称作“更宽的类型”)**就可以了。例如9.8+1
,Python会把1
隐式地转换为浮点数。
强制转换是一种隐式转换,不需要程序员的参与,当然Python也提供了一些显式的类型转换函数,分别为int(),float(),complex()
- true除法和floor除法
在Python2.2之前的版本中,表示除法的运算符只有/
这一个,它可以进行两种类型的除法:
- floor除法:即“地板除”,两个操作数都是整数,返回的是不大于正确结果的最大整数。有一些教程中会说截取了结果的小数部分,这种说法是恰当的,因为结果为负数的时候不符合这个规则:
#Python 2.7.13
>>> 1 / 2
0
>>> -1 / 2
-1 #结果是-1而不是0
- true除法:只要有一个操作数是浮点数,结果返回的就是浮点数:
#Python 2.7.13
>>> 1.0 / 2
0.5
>>> 1 / 2.0
0.5
>>> 1 / float(2)
0.5
在Python2.2之后,设计者们为了使语言更加简洁明了,就将floor除法的功能剥离出来,用//
来表示。这样,无论操作数是整数还是浮点数,//
都将执行floor除法:
>>> 1 // 2
0
>>> 1.0 // 2
0
>>> 1 // 2.0
0
不过,只要你使用的是Python2.x,你就会发现/
仍然可以表示true除法和floor除法。在Python3.x中,/
才变成了仅代表true除法:
#Python 3.x
>>> 1 / 2
0.5
>>> 1.0 / 2
0.5
>>> 1 / 2.0
0.5
如果你希望在Python2.x中让/
只表示true除法,可以在程序中加入这样一条语句from __future__ import division
。
- 取余运算
取余只需要记住一个要点:取余过程中的商一定是整数,并且是通过floor除法得到的整数。
- 幂运算
幂运算**
需要注意优先级问题。
首先,一元操作符(+,-代表取正,取负)的优先级大于加减乘除和取余(+,-,*,/,//,%)运算,可以用一个例子来理解:
>>> -1 // 2
-1
>>> -(1 // 2)
0
这里的-
表示取负数,优先级一定大于//
,编写程序的时候保险起见也可以加上括号,-1 // 2
其实也就等价于(-1) // 2
幂运算**
比较特殊,它会比左侧操作数的一元运算符优先级高,这也就是说-1 ** 2
与(-1) ** 2
不等价:
>>> -1 ** 2 #幂运算先计算
-1
>>> (-1) ** 2
1
另外,除了运算符**
,Python还提供了内置函数pow()
用于乘方运算。
传入两个参数时,pow(x, y)
等价于x**y
。
传入三个参数时,且x,y,z都为整数时,pow(x, y, z)
与x ** y % z
等价,而当x,y,z有一个不是整数时,pow(x, y, z)
会报错:
>>> pow(3, 6, 6.0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: pow() 3rd argument not allowed unless all arguments are integers
此外,math模块中还提供了sqrt()函数,用于开平方,不过完全可以用pow()来代替:
from math import * #为了使用sqrt()必须导入math模块,pow则不用
>>> sqrt(8)
2.8284271247461903
>>> pow(8,0.5) #开平方
2.8284271247461903
>>> pow(8,1/3) #开三次方
2.0
5.5.2 比较运算符
比较运算符包括==,!=,>,<,>=,<=
,运算返回的结果是bool值(True
或False
),常用在条件判断中,例如if a != b:…
5.5.3 逻辑运算符
逻辑运算符包括and,or,not
,分别对应与、或、非。也常用在条件判断中,例如if a and b:…
if not b:…
注意Python中逻辑运算不会直接返回bool值(True
或False
),实际规则如下:
逻辑表达式 | 返回规则 |
---|---|
x and y | 如果 bool(x) 为 False,x and y 返回 x,否则返回 y 。 |
x or y | 如果 bool(x) 为 True,x and y 返回 x,否则返回 y 。 |
not x | 如果 bool(x) 为 False,not x 返回 True,否则返回 False 。 |
5.5.4 成员运算符
成员运算符包括in,not in
,可以用于判断特定的对象是否存在于序列中中,运算返回的结果是bool值(True
或False
)
>>> 1 in [1, 2, 3]
True
>>> 'd' in "abc"
False
in
一般会与for
连用,用于遍历序列,例如下面这个例子:
#判断list_1的哪些字符存在于list_2中
list_1 = ['A','B','C','D']
list_2 = ['A','B']
for letter in list_1:
if letter in list_2:
print(letter+' is exists in list_2')
else:
print(letter+' is not exists in list_2')
运行结果:
A is exists in list_2
B is exists in list_2
C is not exists in list_2
D is not exists in list_2
5.5.5 身份运算符
身份运算符包括is,is not
,用于判断两个变量是否引用自同一个对象,因此,a is b
也就等价于 id(a) == id(b)
。
5.5.6 赋值运算符
赋值运算在第二章中已经作为基本语法介绍过,主要包括的就是普通的赋值运算=
,以及结合算术运算符的增量运算+=,-=,*=,/=,//=,%=,**=
。
5.5.7 位运算符
还有一个初学时不常用,但实际上非常实用的运算符,就是位运算符。位运算中包含运算符&,|,^,~,<<,>>
首先要记住的是,位运算的操作数都是整数,并且运算针对的是整数的二进制形式。
先介绍&,|,^
这三个运算符,它们分别被称为“按位与”、“按位或”、“按位异或”。先说&
,实际上就是把左右两个操作数的二进制形式逐位进行与运算,得到一个新的二进制数,用一个例子来理解:
>>> a = 60 # 60 = 0011 1100
>>> b = 13 # 13 = 0000 1101
>>> a & b
12 # 12 = 0000 1100
以此类推,|、^
就是将每一位上的与运算改为或运算和异或运算即可。
~
运算符的作用是“按位取反”,顾名思义,就是将二进制的每一位1变为0,0变为1。
<<,>>
是左移运算符和右移运算符,用于将运算数的各二进位全部左移或右移若干位
>>> a = 13 # 13 = 0000 1101
>>> a << 1
26 # 26 = 0001 1010
>>> a << 2
52 # 52 = 0011 0100
>>> a >> 1
6 # 6 = 0000 0110
除了各类运算符之外,还有一些针对数字类型对象的内置函数需要了解
5.5.8 舍入函数
- floor(x)
floor代表地板,那么顾名思义,就是在传入参数的“下面”,即取不大于传入参数的最大整数:
>>> floor(1.3)
1
>>> floor(-1.3)
-2
- ceil(x)
ceil代表房顶,就是在传入参数的“上面”,即取不小于传入参数的最小整数:
>>> ceil(1.3)
2
>>> ceil(-1.3)
-1
- round(x, [, n] ) (这里x不能缺省,代表目标操作数,而n是可以缺省的,代表保留几位小数,缺省为0)
人们有时候会说round()的作用是四舍五入:
>>> round(8.2)
8
>>> round(-9.6)
-10
round()也可以指定保留几位小数:
>>> round(8.5)
8
>>> round(56.659, 1)
56.7
不过在使用的时候,可能会发现一些问题:
在python2.x中运行:
>>> round(2.5)
3.0
>>> round(-2.5)
-3.0
在python3.x中运行:
>>> round(2.5)
2
>>> round(-2.5)
-2
>>> round(3.5)
4
>>> round(-3.5)
-4
因此准确来说,round()一方面可以完成的是四舍六入的功能;另一方面,当传入的数字与两侧整数距离相同的时候,python2.x的做法是:保留到离0较远的一侧,而python3.x的做法是:保留到偶数的一侧。
需要注意的是,上述规则适用于传入的数字与两侧整数距离相同的时候,那么按理来说,无论python2.x还是python3.x,round(2.675, 2)
的结果都应该是2.68
,但是实际上结果却是2.67
!这主要是因为2.675转换为二进制之后,小数点后面的位数太多了(大约是10.10101100110011001100110011001100110011001100……),超出了计算机能够表示的精度,因此小数点后面的一串二进制数已经被截断了一部分,因此2.675在计算机中并不能精确表示,而是离2.67更近一些,使用round()时直接舍到2.67即可。
可见,round()结果会受到计算机本身表示精度的影响,因此可能会带来一些意想不到的结果,如果对精度要求高的计算,尽量避免使用round()
,一些替代的方案可以参考这篇blog:
http://www.runoob.com/w3cnote/python-round-func-note.html
5.5.9 random模块
random模块中包含了多个生成随机数对象的函数,是Python中的一个非常常用的模块。
在使用random模块前需要导入:
import random
这里提一个代码风格上的问题。我们在之前导入math模块的时候使用的是from math import *
同样我们可以使用import math
,区别在于前者我们可以直接调用模块的内置函数,例如sqrt()
;而后者这样导入,我们必须采用模块.函数的格式调用内置函数:math.sqrt()
。Python的技术规格中更建议使用后者,这是因为不同模块中可能有同名的函数,为了避免混淆,在调用模块内置函数的时候,最好还是在函数名前面加上模块名。
回到random模块,主要的几个函数如下:
- random.random()
生成一个0到1之间的随机浮点数,包括0但不包括1,也就是[0.0, 1.0)
>>> random.random()
0.02025527401777727
- random.randint(x, y)
随机生成[x,y](包括x, y)区间内的整数,需要注意x,y都必须是整数,且x<=y。
>>> random.randint(1,9)
4
- random.uniform(x, y)
随机生成 [x, y) 或[y,x)区间内的浮点数,注意x,y可以不是整数,并且不用考虑大小
>>> random.uniform(10.6,1.5)
9.664643121543374
- random.randrange ([start,] stop [,step])
该方法返回[start, stop),并且递增基数为step的集合内的一个随机整数。start缺省值为0,step缺省值为1。注意start, stop, step都必须为整数。
>>> random.randrange(1, 100, 2) #从1-99中选一个奇数
93
>>> random.randrange(100) #从0-99中选一个随机整数
2
>>> random.randrange(0, 100, 5) #以5为递增基数,从0-99中选一个随机数
45
以上四个函数用于生成随机数,下面看一个能够控制随机数生成的函数
- random.seed([x])
传入该函数一个固定的seed值,就能够让随机数固定:
>>> random.seed( 10 )
>>> random.random()
0.5714025946899135
>>> random.seed( 10 )
>>> random.random()
0.5714025946899135
>>> random.seed( 10 )
>>> random.random()
0.5714025946899135
这其实也说明了random模块中的函数生成的是伪随机数,即生成随机数的算法是固定的,改变的只是算法的参数seed值,如果不手动设定seed值,Python就会根据系统时间自行设定seed值。
接下来再看几个与序列有关的函数,序列将会在之后的章节中详细介绍,这里提前接触一下。Python中序列也就是三种:list(列表)、tuple(元组)、string(字符串)。
- random.choice(seq)
从序列中随机选取一个元素,并返回这个元素,seq应当是一个序列,
>>> random.choice([1, 3, 4, 6, 9]) #list(列表)
1
>>> random.choice('a string') #string(字符串)
't'
>>> random.choice((1, 4, 5, 8)) #tuple(元组)
8
- random.sample(seq, n)
从序列中随机选取n个元素形成一个新的list,并返回这个list,注意n不能大于原序列seq的长度。
>>> random.sample([1, 3, 4, 6, 9], 3)
[1, 9, 6]
>>> random.sample('a string', 3)
['g', 'i', 'a']
>>> random.sample((1, 4, 5, 8), 3)
[8, 1, 4]
需要注意,无论是random.choice()
还是random.sample()
,都不会改变传入的原序列,而以下这个函数则不同。
- random.shuffle(seq)
该函数可以将序列seq中的元素打乱,random.shuffle()
没有返回值,它是直接改变原序列seq。
>>> list_1 = [1, 3, 4, 6, 9]
>>> random.shuffle(list_1) ##本身没有返回值
>>> list_1
[9, 1, 4, 6, 3]
>>> print(random.shuffle(list_1)) ##再次证明本身没有返回值
None
本章的内容较多,梳理一下结构: