Python 运算符与优先级
运算符
几乎任何语言的运算符,在基础语法上都是最为简单的,但是在很多算法当中往往有着妙用。
这篇笔记从运算符的优先级开始:下面从优先级开始认识 Python 中的运算符,并从各个类型进行剖析其中的代表运算符,语法和一些注意事项。
运算符优先级
下表对 Python 中运算符的优先顺序进行了总结,从最高优先级(最先绑定)到最低优先级(最后绑定)。除非语法显式地指明,否则运算符均为双目运算符。 相同单元格内的运算符从左至右组合的(只有幂运算符是从右至左组合的)。
运算符 | 描述 | 类型 | 级别 |
---|---|---|---|
(expressions...) ,[expressions...] , {key: value...} , {expressions...} | 绑定或加圆括号的表达式,列表显示,字典显示,集合显示 | 括号 | 甲子 |
x[index] , x[index:index] , x(arguments...) , x.attribute | 抽取,切片,调用,属性引用 | 括号 | 甲丑 |
await x | await 表达式 | 表达式 | 乙 |
** | 乘方 | 算术运算 | 丙子 |
+x , -x , ~x | 正,负,按位非 | 位运算 | 丙丑 |
* , @ , / , // , % | 乘,矩阵乘,除,整除,取余 | 算术运算 | 丙寅 |
+ , - | 加和减 | 算术运算 | 丙卯 |
<< , >> | 移位 | 位运算 | 丙卯 |
& , ^ , | | 按位与、异或、或 | 位运算 | 丙卯 |
in , not in , is , is not , < , <= , > , >= , != , == | 比较运算:包括成员检测和标识号检测 | 比较运算 | 丁 |
not x | 布尔逻辑非 | 逻辑运算 | 戊子 |
and | 布尔逻辑与 | 逻辑运算 | 戊丑 |
or | 布尔逻辑或 | 逻辑运算 | 戊寅 |
if – else | 条件表达式(不是条件语句) | 表达式 | 己 |
lambda | lambda 表达式 | 表达式 | 庚 |
:= | 赋值表达式 | 表达式 | 辛 |
- 括号 >
await
> 乘方 > 一元 > 算术 = 位 > 比较 > 逻辑 > 表达式
算术运算
就是常规的数学意义上的值运算,优先级也是 乘方 > 求正负 > 乘除求余 > 加减
。
求正负
正值、负值和按位取反运算符都是 一元运算符
。
-
(负值) 运算符:求负;通过__neg__()
特殊方法来重载。+
(正值) 运算符:求正,几乎不使用,C++中某些情况还可以有整数提升的作用;通过__pos__()
特殊方法来重载。
x = -10.0
print(+x) # -10.0
print(-x) # 10.0
加减乘除
数值上的加减乘除没有意思,下面的案例,自定义一个字符串类(只含有大小写字母),重载加减乘除:
详细的描述有点繁琐,具体效果可看代码注释。
- 重载
__add__
:添加合并。 - 重载
__sub__
:减掉一个包含在内的字母。 - 重载
__mul__
:包含在内的字母个数相乘。 - 重载
__truediv__
:减掉全部包含在内的字母。
class OrderedStr:
def __init__(self, string: str=''):
"""根据ASCII码排序"""
self.string = ''.join(sorted([*string]))
def __add__(self, other):
return OrderedStr(self.string + other.string)
def __sub__(self, other):
s = [*self.string]
[s.remove(i) for i in set(other.string) if s.count(i)]
return OrderedStr(''.join(s))
def __mul__(self, other):
s = [*self.string]
[s.append(i * ((other.string.count(i)-1)*self.string.count(i))) for i in set(other.string) if s.count(i)]
return OrderedStr(''.join(s))
def __truediv__(self, other):
s = [*self.string]
for i in set(other.string):
while True:
if s.count(i):
s.remove(i)
else:
break
return OrderedStr(''.join(s))
def __str__(self):
return self.string
def main():
s1 = OrderedStr("xyzabccbb")
s2 = OrderedStr("bab")
print(s1) # abbbccxyz
print(s1 + s2) # aabbbbbccxyz
print(s1 - s2) # bbccxyz
print(s1 * s2) # abbbbbbccxyz
print(s1 / s2) # ccxyz
if __name__ == '__main__':
main()
这里主要是要说明可以重载运算符。
整除与取余
对于整数,和小学数学中的描述一样,除数 ÷ 被除数 = 商 ... 余数
;
对于浮点数,商是和余数都是浮点数,除数 = 整数大小是商 * 被除数 + 余数
,余数往往不是理想值,这是由于浮点型在内存中的存储方式导致的。
print(10 // 3) # 3
print(10 % 3) # 1
print(7.2 // 3) # 2.0
print(7.2 % 3) # 1.2000000000000002
print(7.2 // 2.4) # 3.0
print(7.2 % 2.4) # 4.440892098500626e-16 不是0
%
也被用在字符串格式化。
乘方
乘方运算的底数和指数都可以是浮点数,也可以是负数。
print(2 ** 3) # 2^3 == 8
print(2.1 ** 3) # 9.261000000000001 底数是浮点数
print(2 ** 3.5) # 11.313708498984761 指数是浮点数
print(2 ** -1) # 0.5
print(-2 ** 3) # -8
- 幂运算符的绑定比在其左侧的一元运算符更紧密;但绑定紧密程度不及在其右侧的一元运算符。比如
2 ** ~-1
,先后给-1
绑定的顺序是-
,~
,**
。 - 可使用
__pow__()
进行重载。
矩阵乘法
矩阵乘法也是一个一般只在数学领域才会使用到的运算。下面仅给出一个例子:
import numpy as np
r = np.array([[1, 2, 3, 4]])
c = np.array([[1], [2], [3], [4]])
print(r @ c) # 1^2+2^2+3^2+4^2==30
位运算
位运算和逻辑运算都有 与或非
三种典型运算,不同的是,逻辑运算更多的是对语句或表达式判断真假值,而位运算只是基于数据内容进行以比特位为单元的操作。
非、与、或、异或
- 非(NOT)运算具有更高的优先级,是一元运算符,其表现为
按位取反
。 - 与(AND)运算
按位与
,经常用来保留数据的某些比特位置,比如说保留最后一个比特位置:x & 1
。 - 或(OR)运算
- 异或(XOR)运算
x, y = 0b1101, 0b1001 # 0b 开始的二进制数
print(~x) # 0b0010 == 2
print(x & y) # 0b1001 == 9
print(x | y) # 0b1101 == 13
print(x ^ y) # ob0100 == 4
- python 中对与数字应该和 JavaScript 部分一样,先将数字转成字符串,再去判断值,所以
1
和1.0
有相同的哈希值,在作为字典的索引时,表示相同的内容。即对于 d = {1: ‘a’},d[1] 和 d[1.0] 都能解读到 ‘a’。 0b1101
赋值给 x ,相当于二进制字符串 ‘0b1101’ 解读成 13 在进行赋值;更多的,八进制字符串以0o
开头,十六进制字符串以0x
开头。- 可使用
__not__()
、__and__()
、__or__()
、__xor__()
进行自定义。
移位
移位分为左移位和右移位,左移位往高位移动,移位 1 次相当于乘 2 ;右移位往低位移动,移位 1 次相当于整除 2 。
x = 0b11010010 # 210
print(x >> 1) # 210/2 == 105 0b01101001
print(x << 1) # 210*2 == 420 0b110100100
- 移位运算的优先级低于算术运算。
- 使用
__lshift__()
和__rshift__()
方法来自定义。
比较运算
比较运算:值运算、成员检测、标志号比较,返回布尔值 True 或 False。
自定义的 比较方法 可以不返回布尔值。
比较运算的连串、嵌套。
值比较
值比较使用运算符 <
, >
, ==
, >=
, <=
和 !=
将两个对象的值进行比较。
- 相等比较时,相等的对象应该或者具有相同的哈希值
hash(x) == hash(y)
。
成员检测
成员检测运算符包含 in
、not in
,如果 x 是 s 的成员则 x in s
求值为 True,否则为 False。 x not in s
返回 x in s 取反后的值。
- 所有内置序列和集合类型以及字典都支持此运算,对于字典来说 in 检测其是否有给定的键。
标志号比较
标识号比较的运算符包括 is
和 is not
,用于检测对象:当且仅当 x 和 y 是同一对象时 x is y
为真。 一个对象的标识号可使用 id()
函数来确定。 x is not y
会产生相反的逻辑值。
逻辑运算
运算符 not x
将在 x 为真 False
,否则 True
。
表达式 x and y
首先对 x 求值;如果 x 为假则返回该值;否则对 y 求值并返回其结果值。换句话说,两者为真才为真,出现假值直接返回,求值顺序从左到右。
表达式 x or y
首先对 x 求值;如果 x 为真则返回该值;否则对 y 求值并返回其结果值。换句话说,两者真有则为真,出现真值直接返回,求值顺序从左到右。
注意:
not
将创建一个新值,必须是一个布尔值。例如:not ""
返回 True,不能返回字符串或其他任何值。and
和or
表达式的返回值不必为布尔值,而是返回最后被求值的值。例如:
x, y = 1, 3
x == 1 and y > 1 # True, x==1为真,继续求y>1的值,最后返回y>1的值
x == 1 and y # 3 , x==1为真,继续求y的值,最后返回y的值
x == 1 or y < 1 # True, x==1为真,不继续求值,直接返回真
x != 1 or y # 3 , x!=1为真,继续求y的值,最后返回y的值
表达式运算
条件表达式
条件表达式(有时称为**“三元运算符**”),具有最低的优先级。
其格式 x if C else y
首先是对条件进行判断。 如果条件为真,x 将被求值并返回其值;否则将对 y 求值并返回其值。
x = 1
y = 2*x if x % 2 else x # 奇数取2倍,偶数取本身
赋值表达式
赋值表达式(命名表达式、海象表达式)将一个表达式赋值给一个标识符,同时还会返回表达式的值。
这是一个很不常用的表达式,赋值语句有更好理解的语法和适用性。
lambda
表达式
完全可以理解为一个轻巧的函数。
def lambda(*args)
return expression
f1 = lambda
f2 = lambda *args: expression # f1, f2 具有相同的作用
- lambda 表达式被用于创建匿名函数,生成一个函数对象。
expression
只能是表达式,不能包含任何语句
await
表达式
挂起一个 awaitable
对象等待执行,只能在协程函数内部使用。一个简单的例子如下:
from asyncio import run
async def get_now():
import datetime
return datetime.datetime.now()
async def show_now():
now = await get_now()
print(now)
if __name__ == '__main__':
run(show_now())