2.2 Python运算符与表达式
Python是面向对象的编程语言,在Python中一切都是对象。对象由数据和行为两部分组成,而行为主要通过方法来实现,通过一些特殊方法的重写,可以实现运算符重载。运算符也是表现对象行为的一种形式,不同类的对象支持的运算符有所不同,同一种运算符作用于不同的对象时也可能会表现出不同的行为,这正是“多态”的体现。
在Python中,单个常量或变量可以看作最简单的表达式,使用除赋值运算符之外的其他任意运算符和函数调用连接的式子也属于表达式。
除了算术运算符、关系运算符、逻辑运算符以及位运算符等常见运算符之外,Python还支持一些特有的运算符,例如成员测试运算符、集合运算符、同一性测试运算符等。使用时需注意,Python很多运算符具有多种不同的含义,作用于不同对象时的含义并不完全相同,非常灵活。常用的Python运算符如表2-3所示,运算符优先级遵循的规则为:算术运算符的优先级最高,其次是位运算符、成员测试运算符、关系运算符、逻辑运算符等,算术运算符遵循“先乘除,后加减”的基本运算原则。虽然Python运算符有一套严格的优先级规则,但是强烈建议在编写复杂表达式时使用圆括号说明其中的逻辑来提高代码可读性。记住,圆括号是明确和改变表达式运算顺序的利器,在适当的位置使用括号可以使得表达式的含义更加明确。
表2-3 Python运算符
2.2.1 算术运算符
(1)+运算符除了用于算术加法以外,还可以用于列表、元组、字符串的连接,但不支持不同类型的对象之间相加或连接。
>>>[1,2,3]+[4,5,6] #连接两个列表 [1,2,3,4,5,6] >>>(1,2,3)+(4, ) #连接两个元组 (1,2,3,4) >>>'abcd'+'1234' #连接两个字符串 'abcd1234' >>>'A'+1 #不支持字符与数字相加,抛出异常 TypeError: Can't convert'int'object to str implicitly >>>True+3 #Python内部把True当作1处理 4 >>>False+3 #把False当作0处理 3
(2)*运算符除了表示算术乘法,还可用于列表、元组、字符串这几个序列类型与整数的乘法,表示序列元素的重复,生成新的序列对象。字典和集合不支持与整数的相乘,因为其中的元素是不允许重复的。
>>>True*3 3 >>>False*3 0 >>>[1,2,3]*3 [1,2,3,1,2,3,1,2,3] >>>(1,2,3)*3 (1,2,3,1,2,3,1,2,3) >>>'abc'*3 'abcabcabc'
(3)运算符/和//在Python中分别表示算术除法和算术求整商(floor division)。
>>>3/2 #数学意义上的除法 1.5 >>>15//4 #如果两个操作数都是整数,结果为整数 3 >>>15.0//4 #如果操作数中有实数,结果为实数形式的整数值 3.0 >>>-15//4 #向下取整 -4
(4)%运算符可以用于整数或实数的求余数运算,还可以用于字符串格式化,但是并不推荐这种用法,关于字符串格式化的详细介绍请参考第7章。
>>>789 % 23 #余数 7 >>>123.45 % 3.2 #可以对实数进行余数运算,注意精度问题 1.849999999999996 >>>'% c, % d'% (65,65) #把65分别格式化为字符和整数 'A,65' >>>'% f, % s'% (65,65) #把65分别格式化为实数和字符串 '65.000000,65'
(5)**运算符表示幂乘,等价于内置函数pow()。例如:
>>>3**2 #3的2次方,等价于pow(3,2) 9 >>>pow(3,2,8) #等价于(3**2)% 8 1 >>>9**0.5 #9的0.5次方,即9的平方根 3.0 >>>(-9)**0.5 #可以计算负数的平方根 (1.8369701987210297e-16+3j)
2.2.2 关系运算符
Python关系运算符最大的特点是可以连用,其含义与我们日常的理解完全一致。使用关系运算符的一个最重要的前提是,操作数之间必须可比较大小。例如,把一个字符串和一个数字进行大小比较是毫无意义的,所以Python也不支持这样的运算。
>>>1<3<5 #等价于1<3 and 3<5 True >>>3<5>2 True >>>1>6<8 False >>>1>6<math.sqrt(9) #具有惰性求值或者逻辑短路的特点 False >>>1<6<math.sqrt(9) #还没有导入math模块,抛出异常 NameError: name'math'is not defined >>>import math >>>1<6<math.sqrt(9) False >>>'Hello'>'world' #比较字符串的大小 False >>>[1,2,3]<[1,2,4] #比较列表的大小 True >>>'Hello'>3 #字符串和数字不能比较 TypeError: unorderable types: str()>int() >>>{1,2,3}<{1,2,3,4} #测试是否子集 True >>>{1,2,3}=={3,2,1} #测试两个集合是否相等 True >>>{1,2,4}>{1,2,3} #集合之间的包含测试 False >>>{1,2,4}<{1,2,3} False >>>{1,2,4}=={1,2,3} False
2.2.3 成员测试运算符in与同一性测试运算符is
成员测试运算符in用于成员测试,即测试一个对象是否为另一个对象的元素。
>>>3 in [1,2,3] #测试3是否存在于列表[1,2,3]中 True >>>5 in range(1,10,1) #range()是用来生成指定范围数字的内置函数 True >>>'abc'in'abcdefg' #子字符串测试 True >>>for i in (3,5,7): #循环,成员遍历 print(i, end='\t') 3 5 7
同一性测试运算符(identity comparison)is用来测试两个对象是否是同一个,如果是则返回True,否则返回False。如果两个对象是同一个,两者具有相同的内存地址。
>>>3 is 3 True >>>x=[300,300,300] >>>x[0] is x[1] #基于值的内存管理,同一个值在内存中只有一份 True >>>x=[1,2,3] >>>y=[1,2,3] >>>x is y #上面形式创建的x和y不是同一个列表对象 False >>>x[0] is y[0] True >>>x.append(4) #不影响列表y的值 >>>x [1,2,3,4] >>>y [1,2,3] >>>x=y #x和y指向同一个对象 >>>x is y True >>>x.append(4) #对x进行操作会对y造成同样的影响 >>>x [1,2,3,4] >>>y [1,2,3,4]
2.2.4 位运算符与集合运算符
位运算符只能用于整数,其内部执行过程为:首先将整数转换为二进制数,然后右对齐,必要的时候左侧补0,按位进行运算,最后再把计算结果转换为十进制数字返回。位与运算规则为1&1=1、1&0=0&1=0&0=0,位或运算规则为1|1=1|0=0|1=1、0|0=0,位异或运算规则为1^1=0^0=0、1^0=0^1=1。另外,左移位时右侧补0,每左移一位相当于乘以2;右移位时左侧补0,每右移一位相当于整除以2。
>>>3<<2 #把3左移2位 12 >>>3&7 #位与运算 3 >>>3|8 #位或运算 11 >>>3^5 #位异或运算 6
集合的交集、并集、对称差集等运算借助于位运算符来实现,而差集则使用减号运算符实现(注意,并集运算符不是加号)。
>>>{1,2,3}|{3,4,5} #并集,自动去除重复元素 {1,2,3,4,5} >>>{1,2,3}&{3,4,5} #交集 {3} >>>{1,2,3}^{3,4,5} #对称差集 {1,2,4,5} >>>{1,2,3}-{3,4,5} #差集 {1,2}
2.2.5 逻辑运算符
逻辑运算符and、or、not常用来连接条件表达式构成更加复杂的条件表达式,并且and和or具有惰性求值或逻辑短路的特点,当连接多个表达式时只计算必须要计算的值。例如,表达式exp1 and exp2等价于exp1 if not exp1 else exp2,而表达式exp1 or exp2则等价于exp1 if exp1 else exp2。在编写复杂条件表达式时充分利用这个特点,合理安排不同条件的先后顺序,在一定程度上可以提高代码的运行速度。另外要注意的是,运算符and和or并不一定会返回True或False,而是得到最后一个被计算的表达式的值,但是运算符not一定会返回True或False。
>>>3>5 and a>3 #注意,此时并没有定义变量a False >>>3>5 or a>3 #3>5的值为False,所以需要计算后面的表达式 NameError: name'a'is not defined >>>3<5 or a>3 #3<5的值为True,不需要计算后面的表达式 True >>>3 and 5 #最后一个计算的表达式的值作为整个表达式的值 5 >>>3 and 5>2 True >>>3 not in [1,2,3] #逻辑非运算not False >>>3 is not 5 #not的计算结果只能是True或False之一 True >>>not 3 False >>>not 0 True
2.2.6 矩阵乘法运算符@
从Python 3.5开始增加了一个新的矩阵相乘运算符@,不过由于Python没有内置的矩阵类型,所以该运算符常与扩展库numpy一起使用。另外,@符号还可以用来表示修饰器的用法,详见5.1.2节。
>>>import numpy #numpy是用于科学计算的Python扩展库 >>>x=numpy.ones(3) #ones()函数用于生成全1矩阵,参数表示矩阵大小 >>>m=numpy.eye(3)*3 #eye()函数用于生成单位矩阵 >>>m[0,2]=5 #设置矩阵指定位置上元素的值 >>>m[2,0]=3 >>>x@m #矩阵相乘 array([ 6., 3., 8.])
2.2.7 补充说明
除了表2-3中列出的运算符之外,Python还有赋值运算符=和+=、-=、*=、/=、//=、**=、|=、^=等大量复合赋值运算符。例如,x+= 3在语法上等价(注意,在功能的细节上可能会稍有区别,请参考3.1.4节)于x=x+ 3,其他复合赋值运算符与此类似,不再赘述。
Python不支持++和--运算符,虽然在形式上有时候似乎可以这样用,但实际上是另外的含义,要注意和其他语言的区别。
>>>i=3 >>>++i #正正得正 3 >>>+(+3) #与++i等价 3 >>>i++ #Python不支持++运算符,语法错误 SyntaxError: invalid syntax >>>--i #负负得正,等价于-(-i) 3 >>>---i #等价于-(-(-i)) -3 >>>i-- #Python不支持--运算符,语法错误 SyntaxError: invalid syntax >>>-----(3+5) -8 >>>3--5 #等价于3-(-5) 8 >>>3+-5 #等价于3+(-5) -2 >>>3-+5 -2