2.5 运算符和表达式
2.5.1 运算符和表达式的应用方法
程序就是对数据进行处理,表示数据的值(对象)可以用变量给它们起名字(用变量名引用这些值(对象)),对这些变量或值进行处理可使用一些特殊的符号如+、*等进行加法、乘法等运算,这些表示不同运算功能的特殊符号称为运算符(operators)。这些运算符操作的变量或值称为“运算数(operands)”。例如:
2+3 3/(5+2) 3*"world"
用+、/、*、(、)这些运算符对运算数进行运算的式子就是表达式(expression),即表达式是由运算数(值、变量、对象)和运算符构造的。
因为这些表示运算的运算符来自人们熟悉的符号,用它们构成的表达式的含义都很清楚。
前面介绍过,不同类型的对象支持的运算符是不同的,如不能对2个str字符串类型的变量应用-、*、/、//等运算,也不能对1个字符串和1个整数进行如-、+、/等运算。例如,下面的都是非法的:
'hi'-1 'Hello'/123 'hi'*'Hello' '15'+2
即使可以对2个字符串应用+运算符,其含义也不同于2个整数的加法运算,而实际是对2个字符串进行拼接。同样,对1个字符串和1个整数可以应用乘法运算符*,其含义是产生一个重复复制的字符串。例如:
s1="hello" s2=s1+"world" s3 =3*"world" print(s2) print(s3)
输出:
helloworld worldworldworld
不同运算符具有不同的优先级和结合性。例如:
3+5/2 #优先级:先乘除后加减 3+5-2 #结合性:从左向右计算
“3+5/2”中运算符/优先于+,“3+5-2”中+和-具有同样的优先级,但它们的结合性都是“从左向右”,因此,先计算左边的+,再计算右边的-。
2.5.2 运算符的种类
和其他编程语言一样,Python有不同功能的各种运算符,如进行数学计算的“算术运算符”,比较2个运算数大小的“比较运算符”,进行逻辑运算的“逻辑运算符”,对二进制位进行操作的“二进制运算符”,有对变量赋值的“赋值运算符”,还有一些特殊的运算符。
1.算术运算符(arithmetic operators)
表2-1是算术运算符的含义及其示例。需要注意的是,//表示整数除法,而/表示的是浮点除法,%用于求2个整数相除的余数。例如:
表2-1 算术运算符的含义及其示例
x=15 y=2 print('x + y=', x+y) print('x - y=', x-y) print('x * y=', x*y) print('x / y=', x/y) print('x // y=', x//y) print('x ** y=', x**y) print('x % y=', x%y)
输出:
x + y=17 x - y=13 x * y=30 x / y=7.5 x // y=7 x ** y=225 x % y=1
再如:
print(15.3/2.5) print(15.3//2.5) #转为整数除,相当于15//2 print(15.3%2.5) #15.3除2.5的余数 print(15.3**2.5) #15.3的2.5次方
输出:
6.12 6.0 0.3000000000000007 915.6480546203329
2.比较运算符(comparison operators)
表2-2是比较运算符及其含义和示例。
表2-2 比较运算符的含义及其示例
对两个量进行比较,产生的结果是一个表示“真”(True)或“假”(False)的bool(布尔)类型的值。例如:
x=15 y=2 print('x > y is', x>y) print('x < y is', x<y) print('x==y is', x==y) print('x !=y is', x!=y) print('x >=y is', x>=y) print('x <=y is', x<=y)
输出:
x > y is True x < y is False x==y is False x !=y is True x >=y is True x <=y is False
3.逻辑运算符(logical operators)
逻辑运算符and、or、not分别表示逻辑与、逻辑或、逻辑非运算。
在逻辑运算中,True、非0或非空对象就是真(True),而False、0或空对象就是假(False)。
当一个对象x是真(True、非0值或非空值)时,not x就是False,当x是假(False、0或空值)时,not x就是True(真)。例如:
print(not 0) print(not []) print(not False) print(not 2) print(not [3]) print(not True)
输出:
True True True False False False
假设有2个对象x、y,当x是真时,x or y的结果就是x,当x是假时,x or y的结果就是y。
例如:
print(3 or 5) #因为3是真,所以3 or 5的结果就是3 print(3 or 2) #因为3是真,所以3 or 2的结果就是3 print(0 or 2) #因为0是假,所以0 or 2的结果就是2 print(False or True) print(False or []) print({} or 2) #因为{}是假,{} or 2的结果就是2
输出:
3 3 2 True [] 2
假设有2个对象x、y,当x是真时,x and y的结果就是y,当x是假时,x and y的结果就是x。例如:
print(3 and 5) #因为3是真,所以3 and 5的结果就是5 print(3 and 2) #因为3是真,所以3 and 2的结果就是2 print(0 and 2) #因为0是假,所以0 and 2的结果就是0 print(False and True) print(False and []) print({} and 2) #因为{}是假,所以{} and 2的结果就是{}
输出:
5 2 0 False False {}
逻辑运算符and、or、 not的含义及其示例如表2-3所示。
表2-3 逻辑运算符的含义及其示例
这3个逻辑运算符具有不同的优先级,and优先于or, not优先于and、or。例如:
print(2 or 5 and 3) print((2 or 5)and 3)#可用圆括号()改变运算符的计算次序
输出:
2 3
4.位运算符(bit operators)
任何量在计算机内存里都是用1串二进制位表示的。
假如有2个数,a=37和b=22是用1字节表示的,则其内存的二进制位如下所示:
a=0 0 1 0 0 1 0 1 b=0 0 0 1 0 1 1 0
“位运算”:对运算数的二进制位进行相应的运算。其中,&(位与)、|(位或)、^(异或)是对2个运算数的对应二进制位进行运算,而~(取反)、<<(左移位)、>>(右移位)则是对1个运算数的二进制位进行运算。
二元位运算符的运算规则如表2-4所示。3个二元位运算符的运算规则如下。
表2-4 二元位运算符的运算规则
● &(位与)运算:只有p和q都是1时,p&q的结果才是1;如果有一个为0,则结果是0。
● |(位或)运算:只要p和q有一个是1时,p|q的结果就是1;如果p和q都为0,则结果是0。
● ^(异或)运算:当p和q不同(一个是0,另一个是1)时,p^q的结果是1,否则结果是0。
例如,对a、b的&、|、^运算是对它们的对应二进制位进行运算:
a&b =0 0 0 0 0 1 0 0 a |b=0 0 1 1 0 1 1 1 a^b =0 0 1 1 0 0 1 1
下面是对a、b的&、|、^运算的代码。
a=37 b=22 print('a=', '{:08b}'.format(a)) print('b=', '{:08b}'.format(b)) print('a&b=', '{:08b}'.format(a&b)) print('a|b=', '{:08b}'.format(a|b)) print('a^b=', '{:08b}'.format(a^b))
str的format()函数格式化输出a、b、a&b、a|b、a^b的二进制字符串。输出如下:
a= 00100101 b= 00010110 a&b=00000100 a|b=00110111 a^b=00110011
一元位运算符取反~、左移<<、右移>>的运算规则如下。
● ~(取反):将每个二进制取反(0变成1,1变成0)。例如,对x, ~x的结果相当于-x-1,如22的补是-23。
● <<(左移):各二进制位全部左移若干位,高位丢弃,低位补0。
● >>(右移):各二进制位全部左移若干位,无符号数,高位补0。
例如,对b的<<、~运算的结果如下:
b<<2=0 1 0 1 1 0 0 0 ~b=1 1 1 0 1 0 0 1
下面代码输出了对b的<<、>>运算的二进制表示:
print('b= ', '{:08b}'.format(b)) print('b<<2: ', '{:08b}'.format(b<<2)) #b的二进制左移2位,右边低位补充0 print('b>>2: ', '{:08b}'.format(b>>2)) #b的二进制右移2位,左边高位补充0 print('~b: ', '{:08b}'.format(~b)) #~b就是-(b+1), b=22的补是-23 print(b) print(b<<2) print(b>>2) print(~b)
输出如下:
b= 00010110 b<<2: 01011000 b>>2: 00000101 ~b: -0010111 22 88 5 -23
其中的取反运算~涉及1个数在计算机内部的二进制表示。数字在计算机中是以补码保存的,所以Python位运算是作用在补码上的。假如,用8位二进制表示,正整数1,其二进制的原码是00000001,而负整数-1,其二进制的原码是10000001,即左边的最高位1表示这是一个负数。除原码外,1个数在计算机内部还可以用反码和补码表示。正数的反码与其补码和原码是一样的,如1的反码和补码都是0000001,而负数的反码除左边最高位外,其他位都是原码的取反,如-1的反码是11111110,而补码就是在反码基础上加上1,即-1的补码是11111111。
b=22的原码、反码和补码都是00010110。对b的二进制取反的结果就是11101001,这个数实际上是-(b+1)的补码。因为-b的二进制原码是10010110,而-(b+1)的二进制原码是10010111,其补码就是11101001。但bin()和str的fromat()会将~b的补码表示转换成原码表示,因此,尽管~b运算产生的二进制是11101001,但显示的仍是其二进制原码10010111。
5.赋值运算符
赋值运算符=是给1个对象起1个名字,如简单的赋值运算符=的语句“x=3”,就是给对象3起了一个变量名x。例如:
x=2 x=x+3
在表达式“x=x+3”中,右边的变量x引用的是原先的对象,而左边的变量x引用的是新的结果对象。
简单赋值运算符可以和算术、位运算结合构成复合赋值运算符。例如:
x=2 x+=3
“x +=3”实际上是“x=x+3”的简写,即“将x+3的结果赋值给x”,也就是说,给x+3的结果对象起了一个名字x。变量x现在引用的是一个新的结果对象而不是原来的2,可以通过输出x的id来验证这一点:
x=2 y=x # y和x都是对象2的名字 print(x) print(id(x)) x**=3 #相当于x=x**3 print(x) print(id(x)) print(y) print(id(y))
输出:
2 1477476432 8 1477476624 2 1477476432
Python的赋值运算符如表2-5所示。
表2-5 赋值运算符
6.成员运算符(in、not in)
成员运算符in和not in用于判断一个值(对象)是否在一个容器对象,如list、str、tuple对象中,这2个运算符返回的结果是True或False。
alist=[2,5,7,8] print(3 in alist) print(5 in alist) print(3 not in alist) print(5 not in alist)
输出:
False True True False
7.身份运算符:is、is not
身份运算符is和is not用于判断2个变量(标识符)是不是引用的同一个对象。例如:
a=10 b=10 print(id(a)) print(id(b)) print(a is b) print(a is not b)
输出:
1477476688 1477476688 True False
再如:
a=1000 b=1000 print(id(a)) print(id(b)) print(a is b) print(a is not b)
输出:
2417203138160 2417203138192 False True
这是因为,Python解释器执行时会将小整数驻留在内存中,将小整数赋值给多个变量,它们实际引用的是同一个对象。而将整数赋值给不同变量,这些变量将引用临时创建的不同对象。
2.5.3 运算符的优先级
1.运算符的优先级
当一个表达式中有多个运算符时,这些运算符的计算是有先后次序的,即运算符是具有不同的优先级的。例如,算术运算符总是“先乘除,后加减”,同样级别的运算符,如+和-则按照“自左往后的次序”计算。例如:
3+5/2-4
先做除法“5/2”,然后做加法,即用“3”和“5/2”的结果相加,最后才做减法,即前面加法的结果和4相减。
再比如,同样是逻辑运算,not比and优先级高,而and比or优先级高。
实际上,无须记忆这些运算符的优先级,如果不清楚不同运算符的优先级,则可以简单地用圆括号,使其按照希望的意图进行计算。例如:
(3+5)/(2-4)
2.运算符的优先级表
表2-6是Python运算符的优先级表(从上到下优先级从高变低)。
表2-6 Python运算符的优先级表
总结
● 表达式是用运算符对运算数进行运算的式子,运算数包括值或变量,变量实际就是值对象的引用。
● 常见的运算符按照其功能,主要包括算术运算符、逻辑运算符、比较运算符、位运算符。还有一些其他的运算符,如比较运算符(in和not in)、身份运算符(is和is not)。
● 不同的运算符具有不同的优先级,优先级高的先运算。无须记忆运算符的优先级,因为可以用圆括号保证正确的运算顺序。