跳转至

python运算符的使用

add_circle2025-03-02update2025-03-03

1. 常用运算符

1.1 算术运算符

也叫数学运算符,常用于数值之间的计算,当然部分算术运算符也支持容器类型之间的拼接与叠加。

运算符 说明 实例 结果
+ 1+1 2
- 1-1 0
* 1* 3 3
/ 除法(和数学中的规则一样,但计算结果固定为浮点数) 4/2 2.0
// 整除(只保留商的整数部分) 7 // 2 3
% 取余,求模,即返回除法的余数 7 % 2 1
** 幂运算/次方运算,即返回 x 的 y 次方 2 ** 4 16,即 24
var1: int = 7
var2: int = 4

# +
res1: int = var1 + var2
print(res1)

# -
res2: int = var1 -var2
print(res2)

# *
res3: int = var1 * var2
print(res3)

# / (结果是一个小数)
res4: float = var1 / var2
print(res4)
res4: float = 8 / 8
print(res4)

# // (取整数) 两个数在进行地板除的时候,有一个数是小数,就在最后的结果上加.0
res5: int = var1 // var2
print(res5)
res6: int = 8 // 8
print(res6)
res7: int = 28.7 // 5
print(res7)
res8: int = 28 // 5.3
print(res8)

# %
res9: int = var1 % var2
print(res9)
res10: int = 99 % 7
print(res10)

res11: int = -99 % 7  # -1 + 7 = 6 (余数)
print(res11)

res12: int = 99 % -7  #  1 + (-7) = -6 (余数)
print(res12)

# **    
res13: int = var1 ** 3   # var1 * var1 * var1
print(res13)

1.2 赋值运算符

运算符 说 明 用法举例 等价形式
= 最基本的赋值运算 x = y x = y
+= 加赋值 x += y x = x + y
-= 减赋值 x -= y x = x - y
*= 乘赋值 x * = y x = x * y
/= 除赋值 x /= y x = x / y
%= 取余数赋值 x %= y x = x % y
**= 幂赋值 x ** = y x = x ** y
//= 取整数赋值 x //= y x = x // y
:= 变量赋值(海象运算符,用于表达式内部声明变量) x = len(y := input("请输入密码:")) y = input("请输入密码:")
x = len(y)
|= 合并赋值(用于字典拼接操作) x |= y x = {"name": "xiaoming"}
x |= {"age": 16}
&= 按位与赋值 x &= y x = x & y
|= 按位或赋值 x |= y x = x | y
^= 按位异或赋值 x ^= y x = x ^ y
« = 左移赋值 x « = y x = x « y,这里的 y 指的是左移的位数
» = 右移赋值 x » = y x = x » y,这里的 y 指的是右移的位数
# = 所有运算符当中 等号的优先级最低
var1 = 9
var2 = 5
res = var1
# print(res)

# +=
# var1 += var2 
'''var1 = var1 + var2'''
# print(var1)

# -=
# var1 -= var2
'''var1 = var1 - var2'''
# print(var1)

# *=
# var1 *= var2
'''var1 = var1 * var2'''
# print(var1)

# /=
# var1 /= var2
'''var1 = var1 / var2'''
# print(var1)

# //=
# var1 //= var2
'''var1 = var1 // var2'''
# print(var1)

# %=
# var1 %= var2
'''var1 = var1 % var2'''
# print(var1)

# **=
var1 **= var2
'''var1 = var1 ** var2'''
print(var1)

# := 海象运算符,用于在表达式中声明变量
if (n := len(username := input("请输入注册账号:"))) >= 12:
    print(f"账号长度不符。 ({n} 字符, 长度必须低于12个字符。)")
else:
    print(f"输入成功,您输入的账号:{username}")

# 字典合并运算符
data1: dict = {"name": "xiaoming"}
data2: dict = {"age": 16}
data3: dict = data1 | data2
print(data3) # {'name': 'xiaoming', 'age': 16}

# |= 字典合并赋值运算符
data1: dict = {"name": "xiaoming"}
data2: dict = {"age": 16}

data1 |= data2
print(data1) # {'name': 'xiaoming', 'age': 16}

1.3 比较运算符

也叫逻辑比较运算符,计算结果固定为布尔值。

比较运算符 说明
> 大于,如果 > 前面的值大于后面的值,则返回 True,否则返回 False。
< 小于,如果 < 前面的值小于后面的值,则返回 True,否则返回 False。
== 等于,如果 == 两边的值相等,不比较内存地址,则返回 True,否则返回 False。
>= 大于等于(等价于数学中的 ≥),如果 >= 前面的值大于或者等于后面的值,则返回 True,否则返回 False。
<= 小于等于(等价于数学中的 ≤),如果 <= 前面的值小于或者等于后面的值,则返回 True,否则返回 False。
!= 不等于(等价于数学中的 ≠),如果 != 两边的值不相等,则返回 True,否则返回 False。

规律:

  1. 数值类型之间的比较,按数学的规律来比较。
  2. 字符串的比较,是字符在编码的位置数值比较。
  3. 序列类型的比较,是同位成员之间的逐一比较。
  4. 无序类型的比较,是比较双方成员是否一致,不比较顺序,字典不支持大小比较。
# > 
var1: int = 15
var2: int = 18
res: bool = var1 > var2
print(res)
# <
res = var1 < var2
print(res)
# >=  只要满足一个条件即为真
res = var1 >= 15
print(res)
# <=
res = var1 <= 15
print(res)
# ==
res = var1 == var2
print(res)

# !=
res = var1 != var2
print(res)

1.4 成员运算符

成员运算符只有 in 和 not in,返回的结果固定为布尔值,仅针对与容器类型 [字符串、列表、元组、集合、字典的键]。

成员运算符 说明
in 判断一个值是否作为指定容器的成员存在,如果是则返回 True,否则返回 False。
not in 判断一个值是否不在指定容器中作为成员,如果不在则返回 True,否则返回 False。
"""成员运算符"""
data1: str = "hello world"
print("hello" in data1) # True
print("moluo" not in data1) # True
print("hw" in data1) # False

# 列表、集合、元组
data2: list = [1,2,3,4]
print(1 in data2)
# in 和 not in 只会判断值,不会区分具体的数值类型。因此在 in 和 not in 的运算判断中,1 和 1.0 、True 实际上是等价的。
# 同理,0,0.0 与 False 也是等价的
print(1.0 in data2) # True
print(True in data2) # True
print(False not in data2) # True

# 字典,只会判断键
data3: dict = {"name": "xiaoming"}
print("xiaoming" in data3) # False
print("xiaoming" not in data3) # True

1.5 身份运算符

身份运算符只有 is 和 is not 两个运算符,返回的结果固定为布尔值。

成员运算符 说明
is 判断两个变量所引用的内存空间是否相同,如果相同则返回 True,否则返回 False。
is not 判断两个变量所引用的内存空间是否不相同,如果不相同则返回 True,否则返回 False。
a: list = []
b: list = []
print(a is b) # False
print(a is not b) # True

c: int = 10
d: int = 10
print(c is d) # True
print(c is not  d) # False

"""面试题:is 与 == 的区别?
is 用于比较两个变量的内存空间是否是同一个。
== 用于比较两个变量的值是否是相等,不比较内存空间。
"""
print(1 == True) # True
print(1 is True) # False

1.6 逻辑运算符

逻辑运算符,主要用于对 1 个或多个表达式的运算符结果进行联合判断。

image-20250102091527188

逻辑运算符 中文名称 基本格式 说明 [假设 a 与 b 为表达式,真表示成立,假表示不成立]
and 逻辑与,并且 a and b 当 a 和 b 两个表达式的计算结果都为 True 时,a and b 的结果才为 True,否则全部是 False。
or 逻辑或,或者 a or b 当 a 和 b 两个表达式的计算结果都为 False 时,a or b 的结果才是 False,否则全部是 True。
not 逻辑非,取反 not a 如果 a 表达式的计算结果为 True,则 not a 的结果为 False;
反之,如果 a 表达式的计算结果为 False,那么 not a 的结果为 True。
相当于对 a 的结果进行取反。
"""and运算符:并且的意思,满足既要xxx,又要xxx的场景"""
username: str = input("请输入用户名:")
# 用户名长度最多 14 个字符,并且不能留空
# print(len(username) == 0)
# print(len(username) > 14)
# 使用逻辑运算符根据上面的约束判断用户名是否合法
u_len: int = len(username)
print( u_len > 0 and u_len < 14)

"""or运算符:或者的意思,在多个表达式之间,只有一个表达式结果为True,则or表达式的结果为True"""
# 要求用户输入的密码,必须在 7-14 个字符之间,否则表示密码不能使用。
password: str = input("请输入密码:")
# 我们可以使用 or 运算符判断密码是否不合规
p_len: int = len(password)
# print(p_len < 7)
# print(p_len > 14)
print(p_len < 7 or p_len > 14)

"""在python中,逻辑运算符往往是用于在if、for 等语句中充当判断条件表达式"""
username: str = input("请输入用户名:")
u_len: int = len(username)
if u_len >0 and u_len <= 14:
    """合法用户名长度"""
    print("用户名可以合法使用。")

"""not运算符:取反,非,用于把表达式的结果进行取反"""
comdition: bool = 1 < 2
print(not comdition) # False

值得注意的是,与其他编程语言一样,Python 的逻辑运算符会出现逻辑短路现象:

所谓的逻辑短路是指逻辑运算符如果只是根据左边的表达式就能判断出整个逻辑运算的结果,则左边表达式的结果将直接作为逻辑运算的结果,反之,如果左边的表达式不能判断出整个逻辑运算的结果时,则后面的表达式结果将直接作为逻辑运算的结果。

"""逻辑运算符的短路现象:
在使用and、or运算符才出现的问题。
"""
# 当左边表达式为 False 时,此时 and 结果就确定了是 False,则出现短路,因此右边写什么都不会程序执行了。
False and print("hello")
# 当左边表达式为 True 时,那么右边表达式直接可以决定 and 的最终结果,所以短路了,程序直接使用右边的表达式结果作为最终结果。
print(True and 100) # 100

# 当左边表达式为 True 时,此时 or 结果就可以确定是 True 了,则出现短路,不会去执行右边表达式的内容
True or print("hello")
# 当左边表达式为 False 时,那么右边表达式直接可以决定 or 的最终结果,所以短路了,程序直接使用右边的表达式结果作为最终结果。
print(False or "hello") # hello

1.7 位运算符

计算机中,采用二进制(0、1 组成的数字)来进行数据存储的。所谓的位(bit,译作:比特,也叫位模式)运算就是提供给计算机中的二进制数据进行运算的。

位运算符在实际开发中有着广泛的应用场景,但是平常业务并不多见,而是多用于高效处理数据和优化性能的这些底层业务中,如数据结构与算法、数据加解密与数据安全,文件或数据的存储与传输,图像处理或特征提取,硬件接口编程或网络协议等。

在开发中多数情况下,实际上我们是用不上二进制的,但是了解二进制对我们理解计算机底层和编程原理有一定帮助,因此我们需要学习一下。

1.7.1 相关概念

1.7.1.1 减法

要学习二进制运算,首先我们要清楚一个事实,就是计算机 CPU 内部运算器实际上只有加法器,没有减法器。所以 CPU 原则上是不能完成减法运算的。

但是计算机很多功能又依赖减法来完成,因此设计计算机的科学家们就参考了时钟循环的原理,让 CPU 在遇到减法运算时,把这个减法转换成加法来完成。

如果我们要让时钟在 9 点位置,拨回到 2 点位置?应该怎么做?

image-20250102170847074

从上面我们可以看出一个现象,就是在一个固定循环的数字12(模数,表示规模大小)计算范围内:让数字9移动到另一个数字2,除了减去距离数(7)以外,还可以通过加上距离数的补数(5)来完成。也因此在时钟计算过程中,9-7 等价于 9+(12-7),即:a - b = a + (模-b),此时我们可以说,5是7的补数。

img

1.7.1.2 位存储

位(bit,译作:比特),是计算机信息存储的最小单位,是一个二进制位表示,可以是 0 或 1,用于表示数据的大小或存储容量。1 字节(byte) = 8 位(bit)

image-20250102164613391

在上面提到为了解决CPU没有减法器而无法直接进行减法的问题,科学家参考了时钟循环的原理,让 CPU 在遇到减法运算时,把这个减法转换成加法来完成。

因此在计算机中减法问题就变成了加法与负数的问题了。

计算机为了区分正负数,会把二进制中数值左起最高位作为表示当前数值正/负的标记位。

image-20250102164724754

例如,计算机中8位二进制中,表示5 则为:0000 0101, 而-5 则为:1000 0101。

image-20250102183607358

一个字节只有 8 位,所以最多可以表示-127(-2n-1+1,n为位数)~+127(2n-1-1,n为位数),如果数字小于-127 或大于+127 这个范围则会溢出。

image-20250102220301912

因此我们要表示更大的数字,肯定就要使用更大字节容量来表示,例如:16位、32 位或者 64 位。

但是物理存储长度终归是有上限的,所以计算机中的位存储在溢出后也是会出现时钟循环的现象。

例如,在无符号的4位二进制中,能表示的最大数就是15,超过15则归0,则16就是循环数字(模,2n,n为位数)。那么对于13-9而言,则相当于13+(16-9)。

image-20250102205059778

不管上面的过程,至少表面上我们的确是得到了4这个正确结果。所以在4位二进制中,我们可以认为7就是9的补数。

OK,上面的补数,在计算机中,我们也可以叫补码,而计算出补码的原始数字9,我们可以称之为原码。以此可以推论出,在计算机有限物理存储长度中进行二进制运算时,任意一个数字A都有属于它对应的补码A,其他任何数字C对数字A进行减法时,我们都可以使用数字C加上对应的补码A取得正确的结果,即:C - A = C + A,其中A=(模-A)。

1.7.1.3 原码、反码、补码

原码、反码、补码就是为了解决让 CPU 遇到减法运算时转换成加法的二进制数字表示方式。

image-20250103021446656

1.7.1.3.1 原码

原码,即原始二进制码,是一种用于表示整数的二进制原始编码方式。在原码表示法中,最高位通常被用作符号位:0 表示正数,1 表示负数。其余位则直接表示该数的绝对值。例如,对于一个8位的整数:

  • 5 的原码:00000101
  • -5 的原码:10000101

image-20250102183607358

注意:

因为原码使用左起最高位表示正负号,因此这种设计造成CPU表示0存在2种原码的写法,这破坏了数值表示的唯一性。

+0的原码:0000 0000

-0的原码:1000 0000

原码可以直观的表示各种二进制数字,也可以完成各种正数的加法运算,例如:5+7=12。

image-20250103000359115

但是,原码却不能用于完成负数的计算,如下,我要计算5+(-5)的结果。

image-20250102221945677

上面可以看到,5+(-5) = -10,很明显计算错误了。这是什么原因造成的呢?实际上就是上面加法运算中,-5的数字表示有问题导致的。

上面的式子5+(-5)相当于 5-5,按我们前面所说的,按前面所说的CPU需要把减法换成加法,因此应该等于 5 + (模-5) 才对。

上面是8位二进制长度,求得模为2n=28=256,最终通过5 + (256-5),即5+251才能得到正确结果,251的二进制表示为11111011。

image-20250102224429822

当然,上面虽然结果正确了,但是有2个问题得搞清楚才可以:

  1. 上面的11111011在8位二进制长度下,实际上并非251,也不是-5,因此我们称这种通过模计算出来的二进制数字叫补码,也就是说11111011是-5的补码。
  2. 上面的-5的补码11111011是我们手动通过256-5得到的251转换过来的,而CPU是不懂减法的,那么CPU要怎么获取到每个数字的补码呢?
1.7.1.3.2 反码

反码,即是计算机中用于把二进制负数的原码转换到补码的中间码(工具人)。

  • 对于正数:反码就是原码,保持不变。
  • 对于负数:符号位不变,其他位取反。

例如:-5的反码:1000 0101 ---> 1111 1010

image-20250102235742793

注意:

反码是基于原码设计基础上衍生出来的,所以对于0也有2种写法,还是破坏了数值表示的唯一性。

+0的反码:0000 0000

-0的反码:1111 1111

之所以求反码,是因为当前进制位最大值=模-1,即8位有符号二进制中,当前进制位最大值(127)= 模(128)-1 。

image-20250103004000358

而反码+原码=当前进制位最大值。

image-20250103004719382

由上面可得:原码 = 模-1-反码,转换下就是:模 - 原码 = 反码+1,再结合下之前的推论:C - A = C + A,其中A=(模-A),可以得到A=A+1。

1.7.1.3.3 补码

我们平时看到计算机输出的二进制都是原码,而实际上计算机中使用和存储的都是补码。

  • 正数的补码:补码 = 反码 = 原码。

  • 负数的补码:原码转成反码+1。

​ 如果要通过补码得到原码,则负数补码保持标记位不变,其他位取反得到反码,反码-1。

例如:计算-5 的补码。则-5的原码是10000101,反码则为:11111010,反码加1可得:1111 1011

image-20250103012414674

例如:计算-1 的补码。则-1的原码是10000001,反码则为:1111 1110,反码加1可得:1111 1111

image-20250103015428815

补码的出现,解决了原码和反码0在二进制的表示问题,不管是+0,还是-0,都只有1个补码:0000 0000

image-20250103015752374

1.7.2 位运算符

位运算符分2种:位移运算符和按位运算符。

位运算符 中文名称 基本格式 说明
<< 左移 a << b a 的补码向左移动 b 位,右边空出的低位补 0。计算结果=a*2**b
>> 右移 a >> b a 的补码向右移动 b 位,左边空出的高位补标记位(正数补 0,负数补 1),计算结果=a//2**b
& 按位与 a & b ab 两个数的补码进行逐位与操作,只有两个位都是 1 时,结果位才为 1,否则为 0。
| 按位或 a | b ab 两个数的补码进行逐位或操作,只要有一个位是 1,结果位就为 1,否则为 0。
^ 按位异或 a ^ b ab 两个数的补码进行逐位异或操作,两个位不相同时结果位为 1,两个位如果相同则为 0。
~ 按位取反 ~a a 的二进制位进行逐位取反操作,即将所有的 1 变为 0,所有的 0 变为 1。
# a << b  相当于 a x 2 ** b 
res: int = 5 << 2 
"""
    数学:相当于 5 * 2 ** 2 = 20
    CPU:把所有位向左移动2位
        0000 0101[补]
    ←                 2位
    ------------------
        0001 0100[补]
"""
print(res) # 20 

res = -7 << 2
"""
    数学:相当于 -7 * 2 ** 2 = -28
    CPU:把所有位向左移动2位
        1000 0111[原]
        1111 1000[反]
    +                1
        1111 1001[补]
    ←                 2位
    ------------------
        1110 0100[补]  ---> 以下是给人看的,不是CPU中的计算步骤
    -                 1
        1110 0011[反]
        1001 1100[原]
"""

# a >> b  相当于 a  / 2**b
print(13 >> 2) # 3
"""
        0000 1101[补]
    ←                 2位
    ------------------
        0000 0011[补]
"""

# 按位与 &
var1: int = 19
var2: int = 15
print(var1 & var2) # 3
"""
        0001 0011[补]
    & 0000 1111[补]
    ------------------
        0000 0011[补]
"""
# 按位或 |
print(var1 | var2) # 31
'''
        0001 0011[补]
    |   0000 1111[补]
    ------------------
        0001 1111[补]
'''

# 按位异或 ^ " 如果两者不相同,返回真,两者相同返回假
print(var1 ^ var2) # 28
'''
        0001 0011[补]
    ^  0000 1111[补]
    ------------------
        0001 1100[补]
'''

# ~ 按位非,按位取反
print(~19) # -20
"""
    ~  0001 0011[补]
    ------------------
        1110 1100[补]
        1110 1011[反]
        1001 0100[原]
"""

print(~(-19))  # 18
'''
        1001 0011[原]
        1110 1100[反]
    +                1
    ~ 1110 1101[补]
    ------------------
        0001 0010[补,反,原]
'''

注意:我们前面提到过,在 python3.12 版本中,按位或 | 也可以被用于合并字典。

x: dict = {"name": "xiaoming"}
x |= {"age": 16}
print(x)

1.8 运算符的优先级问题

./assets/优先级从高到低.png

顺口溜:

括号内部先算好,指数优先级最高。

按位取反后正负,乘除整除求余数。

加减后面左右移,按位运算先位与。

或和异或两兄弟,比较大小后等于。

遇到赋值先算右,身份成员逻辑,海象运算排最后。