Python入门必学运算符全攻略看完这篇就够了作为教过无数轮Python入门课的人我太清楚新手最容易在哪个地方栽跟头了——不是变量、不是循环、不是函数偏偏是最不起眼的运算符。为什么因为运算符太杂、太碎而且很多教程把它拆得七零八落今天讲算术、明天讲比较、后天补逻辑学完一圈脑子里还是一团乱麻。其实运算符这东西本质上就是“让数据动起来”的规则。你把数字、字符串、布尔值扔进去运算符负责告诉程序怎么处理它们是加减乘除还是比大小还是判断真假。搞清楚这一层你就掌握了Python里最底层的操作逻辑。这篇我打算一次讲透从最基础的算术运算符、赋值运算符、比较运算符、逻辑运算符到很多新手会忽略的成员运算符、身份运算符、位运算符再到运算符优先级这个隐形杀手全部用实际案例串起来。适合刚学完Python基础语法、准备往条件判断和循环挺进的人也适合学了一两个月还稀里糊涂、想回头补课的人。先声明一点我讲的每个点都会给代码示例示例都不长但你最好别只看自己在编辑器里敲一遍。运算符这种东西眼睛会了手不会是最高频的现状。1. 运算符到底是个啥先帮你在脑子里搭个框架很多教材上来就列表格把Python的运算符分成七八类然后逐个给一行示例看起来很全实际看完就忘。这是因为缺少一个整体框架。我的理解是这样运算符就是函数的一种语法糖衣。比如2 3本质上是在调用加法运算逻辑只是Python允许你用更简洁的方式写出来。表面上看语法不同但底层逻辑一样接收两个操作数按照规则处理返回一个结果。把Python运算符按功能分常见的有这么几类类别常用符号解决什么问题算术运算符 - * / // % **数学计算赋值运算符 - * / // % **给变量存值或更新值比较运算符 !判断两个值的大小或相等关系逻辑运算符and or not组合多个真假条件成员运算符in not in判断元素是否在序列中身份运算符is is not判断两个变量是否引用同一对象位运算符^ ~ 如果你做过其他语言比如C、Java你会发现这套体系其实很眼熟。Python在运算符这块并没有搞太多标新立异它真正舒服的地方在于语法更简洁类型更灵活而且能让你把注意力放在业务逻辑上而不是内存细节上。至于为什么必须有类型区分拿算术运算符和赋值运算符举例子就明白了。负责运算负责存值。但实际写代码时你几乎不会只用一个运算符x x 1这个场景太常见了所以Python提供了x 1这种缩写。运算符之间是有分工又有配合的理解了整体框架再学细节你就不会觉得这是死记硬背。2. 算术运算符不只是加减乘除这些细节才是关键2.1 加减乘除与整除取余的配合按正常逻辑先看加减乘除这部分难度低但有几个坑必须说清楚a 17 b 5 print(a b) # 22 print(a - b) # 12 print(a * b) # 85 print(a / b) # 3.4 print(a // b) # 3 print(a % b) # 2 print(a ** b) # 1419857第一坑/号。在Python 3里两个整数相除得到的一定是浮点数哪怕能整除比如10 / 2结果是5.0。很多从C或Java转过来的人在这上面翻过车以为得到的是整数结果类型变成float后面做字符串拼接或类型判断时才发现不对劲。第二坑//是向下取整不是向零取整。这是Python和C/C一个明显的区别。正数时还好说一旦遇到负数print(-17 // 5) # -4而不是 -3 print(17 // -5) # -4为什么因为//的结果始终是不大于商的最大整数。-17 / 5数学上是-3.4向下取整就是-4。这个规则刚接触时非常容易踩尤其是写分页逻辑、分配任务数量这类场景时算出来结果和你预期相差一位可能导致循环多跑一次或少跑一次。第三坑%取余的符号。%的运算规则是a % b a - (a // b) * b。既然//向下取整那取余结果的符号就跟除数一致而不是跟被除数一致print(-17 % 5) # 3不是 -2 print(17 % -5) # -2不是 2这个点很多教程根本不提但它在处理日期、环形队列、分页下标时非常关键。举个例子你要把下标i映射到长度为n的循环队列里如果i可能是负数用Python的取余能天然得到合法的正下标i % n这个特性其实是好用的只是你得知道它存在。2.2 幂运算和它的优先级陷阱**代表幂运算很多人不理解为什么Python要专门搞一个这个明明可以用pow()函数。其实两者有区别**是运算符写起来方便pow()是内置函数支持三个参数时还能做模运算比如pow(2, 10, 1000)直接算2**10 % 1000性能上更好适合大数求模的场景。这里有一个特别容易错的优先级问题print(-2 ** 2) # -4而不是 4 print((-2) ** 2) # 4注意了**的优先级高于一元负号但方向是从右往左结合的。所以-2 ** 2实际是-(2 ** 2)。第一次写的时候很多人都被这个坑过我当初还以为是Python的bug后来查了文档才明白这是运算符优先级刻意设计的。严谨一点工程代码里千万不要写-2 ** 2这种依赖上下文理解的表达式加个括号一劳永逸。2.3 字符串和列表里的算术运算符重载Python里的很多运算符是重载的意思是同一个符号不同类型有不同行为。和*在字符串和列表上表现得非常直观print(Hello World) # Hello World print(ab * 3) # ababab lst1 [1, 2] lst2 [3, 4] print(lst1 lst2) # [1, 2, 3, 4] print(lst1 * 2) # [1, 2, 1, 2]这里有个隐藏知识点字符串的*本质是重复拼接列表的本质是拼接成新列表都不是原地修改。所以如果你在一个循环里频繁做lst lst [x]每次都会生成新列表性能会越来越差。正确做法是用list.append()或先收集再一次性扩展。这是运算符很方便但不能滥用的典型例子。3. 赋值运算符从到:一个比一个讲究3.1 复合赋值运算符的完整清单赋值运算符的核心只有一个。但Python为了方便提供了一堆复合赋值运算符。它们存在的理由很简单代码写起来短而且语义清晰。x 10 x 5 # x x 5 x - 3 # x x - 3 x * 2 # x x * 2 x / 4 # x x / 4 x // 2 # x x // 2 x % 3 # x x % 3 x ** 2 # x x ** 2注意x / 4之后x变成了浮点数即使原来是整数。这个和算术运算符的规则一致。很多新手在写循环累计时用sum sum item其实完全可以用sum item效果一样但可读性更好也少打几个字符。3.2 链式赋值与多重赋值链式赋值和多重赋值是两个很容易混淆但都很实用的写法a b c 100 # 链式赋值三个变量都指向100 x, y 10, 20 # 多重赋值x10, y20 x, y y, x # 交换两个变量的值Python特有的优雅写法链式赋值要稍微留个心眼a b []这种写法会把同一个列表对象同时赋给a和b修改一个另一个也会变。如果你想要两个独立列表应该分开写a []b []。多重赋值的底层其实是元组解包这个机制在Python里特别强大。比如函数返回多个值、遍历字典的键值对时经常用到。不过在解包时要注意左右元素个数必须一致否则会抛出ValueError: too many values to unpack。3.3 海象运算符的真面目Python 3.8引入了海象运算符:学名赋值表达式。它的作用是在表达式中赋值并返回这个值。最经典的场景是while循环里读取数据# 传统写法 data input(请输入: ) while data ! quit: print(f你输入了 {data}) data input(请输入: ) # 海象运算符写法 while (data : input(请输入: )) ! quit: print(f你输入了 {data})看得出来海象运算符把赋值和判断合并了少写了一行重复代码。但它也有争议太多人用它会让代码变得难懂。我的建议是在需要在循环或条件里临时取值并判断这种场景下用效果很好但别为了炫技而刻意使用代码可读性永远是第一位的。4. 比较运算符与逻辑运算符条件判断的两大基石4.1 比较运算符的返回值与链式比较比较运算符的作用是返回一个布尔值True或False。这个返回值非常重要因为它是if、while以及逻辑判断的基础。print(5 3) # True print(5 3) # False print(5 ! 3) # True print(5 5) # True这里有个坑中之坑比较的是值是否相等是赋值。很多新手犯的经典错误是在if里写成if x 3Python会报语法错误这一点比其他语言更早暴露问题其实算好事。Python里还有个非常优雅的语法链式比较这是其他很多语言没有的。age 25 if 18 age 60: print(成年且未退休)这个写法等价于18 age and age 60但更接近自然语言而且只计算了一次age。强烈建议在范围判断时用这种写法既清晰又不容易出错。然后说一下浮点数比较的坑。由于计算机内部用二进制表示浮点数0.1 0.2 0.3的结果是False实际值是0.30000000000000004。解决方案是用math.isclose()import math print(math.isclose(0.1 0.2, 0.3)) # True如果你是做量化交易策略代码、数据分析这类任务记住这个细节能帮你省掉不少莫名其妙的bug。4.2 and、or、not短路逻辑是精髓逻辑运算符有三个and、or、not。大部分教程讲的时候都是拿布尔值举例看起来很简单。但真正的重点其实是短路求值。def check(): print(check 被调用了) return True result False and check() print(result) # Falsecheck 根本没被调用and左边是False时整个表达式已经确定是False右边不会执行。or左边是True时整个表达式已经确定是True右边也不会执行。利用这个特性我们可以写出非常简洁的防御性代码# 只有当 user 不为 None 时才访问 user.name if user is not None and user.name admin: pass如果user是Noneuser.name会抛出AttributeError。但因为有短路逻辑user is not None为False右边的user.name根本不会执行所以不会报错。再看一个经典用法or做默认值。name input(请输入用户名: ) or 匿名用户 print(name)如果输入为空字符串空字符串在布尔上下文中是False所以or会取右边的匿名用户。这是Python里很常见的取默认值写法比写if name : name 匿名用户简洁很多。还有一个重点and和or的返回值并不一定是布尔值而是运算对象本身。a and b如果a为假返回a否则返回ba or b如果a为真返回a否则返回b。这个特性让Python的条件表达式非常灵活但新手容易懵。理解时你可以把逻辑运算看作选值的过程最后的结果直接参与后续计算。4.3 not 与布尔上下文not是一元运算符作用是把结果取反。它还有个隐藏知识点Python里任何对象都可以被判断真假布尔上下文。像0、空字符串、空列表[]、空字典{}、None在布尔上下文中都是False其余大部分是真。所以你会看到很多代码直接写if not lst: print(列表是空的)这个写法比if len(lst) 0更Pythonic也更高效因为不需要调用len()。但注意一个细节not的优先级比较低比比较运算符还低。所以not a b实际上是not (a b)而不是(not a) b。这个和数学上的直觉一致但写的时候建议还是加括号免得自己都看晕。5. 成员运算符、身份运算符判断存在与同一性5.1 in 的多种应用场景成员运算符in和not in用来判断一个元素是否存在于一个序列或集合中。最常见的场景是列表、元组、字符串、字典。fruits [apple, banana, cherry] print(banana in fruits) # True print(grape not in fruits) # True text hello world print(world in text) # True d {name: 张三, age: 20} print(name in d) # True字典默认判断键 print(张三 in d.values()) # True判断值需要用 values()这里字典是一个容易踩坑的点in在字典上判断的是键不是值。你要是想看某个值是否存在必须用d.values()。很多人第一次用字典时在这里懵住以为是Python的bug其实是语义设计如此字典查询最常用的就是查键。in在字符串上的用法也很强大它做的是子串匹配不是单字符匹配。比如lo in hello返回True。如果你要判断文件扩展名、URL路径里的关键词这个运算符非常顺手。5.2 用 is 比较身份不要用来比较数值身份运算符is和is not判断的是两个变量是否指向同一个对象也就是比较内存地址。这跟有本质区别比较内容is比较身份。a [1, 2, 3] b [1, 2, 3] print(a b) # True内容相同 print(a is b) # False两个不同的列表对象 c a print(a is c) # Truec 和 a 指向同一个对象is最常见的场景是判断None。因为None是单例对象所有地方应该用x is None而不是x None。这个习惯在Python社区是被强烈推荐的。需要特别强调一个坑不要用is比较整数或字符串。虽然某些小数范围内的整数和短字符串Python会做缓存让你碰巧发现a is b是True但这种行为依赖具体实现大整数或长字符串可能就变成False了。老老实实用别玩火。那什么时候用is比如自定义类对象、文件对象、单例模式里的实例判断或者copy之后你想确认两个变量是否引用了同一个底层对象。判断单例时is是准确且高效的因为不需要调用对象的__eq__方法直接比指针地址。6. 位运算符从入门到进阶的跨越6.1 位运算的基本操作位运算符新手用得少但它在状态标志、权限控制、图形处理和底层算法里是利器。这类运算符按二进制位操作整数。a 5 # 二进制 101 b 3 # 二进制 011 print(a b) # 1按位与101 011 001 print(a | b) # 7按位或101 | 011 111 print(a ^ b) # 6按位异或101 ^ 011 110 print(~a) # -6按位取反把每一位取反含符号位 print(a 1) # 10左移一位101 - 1010 print(a 1) # 2右移一位101 - 10相当于整除2底层原理其实不复杂。只有两个位都是1时结果位才是1|只要有一个位是1结果位就是1^两个位不同则为1。你可以把整数拆成二进制逐位运算后再合回十进制。~a的结果是-6很多人看到会懵。原因是Python整数是补码表示的~a -a - 1所以~5 -6。理解这一点后你就不会觉得它奇怪了。左移每移一位相当于乘2右移每移一位相当于整除2向下取整。这个特性在做性能优化时很常用尤其是游戏开发、嵌入式、图像处理这些对速度敏感的场景。不过一般业务代码里没必要刻意用位运算代替乘除法Python的*和//已经很快了位运算的优势主要在于它直接操作标志位。6.2 位运算的工程应用权限管理与状态标志光讲原理不够给一个实际能用的场景用位运算做权限组合。假设系统里有三种权限读(1)、写(2)、执行(4)二进制分别对应001、010、100。READ 1 WRITE 2 EXEC 4 # 用户的权限集合 user_perm READ | WRITE # 二进制 011代表可读可写 # 判断是否有写权限 if user_perm WRITE: print(有写权限) else: print(没有写权限) # 增加执行权限 user_perm | EXEC # 取消写权限 user_perm ~WRITE这个方案的好处是一个整数就能表达多种权限状态存储和比较都极快权限组合、增删权限、判断权限都只需要一次位运算。数据库里用一个整数字段保存权限位比用逗号拼接字符串靠谱得多。这种用一个整数的不同二进制位代表不同开关的思路在状态机、配置项、功能开关里也经常出现。你不需要每次都用枚举或者一个布尔列表来管理大量的标志位位域能极大地压缩存储和代码量。6.3 Python位运算与C/Java位运算的差异如果你学过C或Java的位运算符转到Python时会发现几个小差异。一是Python的整数是任意精度的所以位移操作不会因为溢出变成负数或异常你可以放心1 100得到一个巨大的整数二是Python的按位取反~是对有符号整数取反结果带负号不能直接类比Java里的~加上掩码后的行为。不过这些差异对日常使用影响不大核心逻辑一致。对于初学者我的建议是先把、|、^、、这五种掌握好~了解即可实际用到再深入。7. 运算符优先级程序执行的隐形规则7.1 一张表看清优先级前面讲了很多运算符但实际写代码时它们会混在一起。这时候优先级决定了表达式先算哪一部分。如果不清楚优先级你会得到完全出乎意料的结果。下面按从高到低列出常用运算符的优先级优先级运算符说明高**幂运算x -x ~x一元正号、负号、按位取反* / // %乘法、除法、整除、取余 -加法、减法 移位按位与^按位异或 !比较运算符not逻辑非and逻辑与低or逻辑或回忆一个经典示例result 2 3 * 4 ** 2 # 先算 4 ** 2 16 # 再算 3 * 16 48 # 再算 2 48 50 print(result) # 50另一个容易出错的场景是not和比较、逻辑运算符混用a 5 b 10 print(not a b) # not (a b) True print(not a b and b 0) # 先比较再not再and如果你不想费脑子记这个顺序最简单的办法是凡是自己不确定的表达式一律加括号。括号的优先级永远最高而且能直接表达你脑子里的运算顺序。Python官方也推荐用括号提升可读性而不是依赖优先级让你代码变成脑筋急转弯。7.2 实际开发中的优先级误区我见过不少代码因为优先级问题产生隐蔽bug。举一个真实例子判断一个数是否在某范围内有人写if 10 x 20 and x % 2 0: pass这段没问题。但有人为了偷懒写成if 10 x 20 x % 2 0: pass千万别这么写的优先级高于比较运算符所以实际会先算20 x然后x % 2再比较结果完全错乱。这也是为什么我反复强调拿不准就加括号。这会让你像写数学题一样把运算意图明明白白摆出来。还有赋值运算符在表达式里的问题x y 5合法但x 5 y 3不合法。因为赋值运算符的优先级极低并且不能嵌入表达式中。想要在表达式里赋值只能用海象运算符:。7.3 如何训练优先级直觉优先级不用死记硬背多写几个混合表达式错了自然就记住了。但更高效的办法是养成两个习惯第一复杂表达式先拆开。比如result a b * c - d / e你可以在心里拆成result a (b * c) - (d / e)或者直接在代码里写成这样。第二凡是包含了三种及以上不同运算符的表达式都用括号把子表达式包起来。代码不是越短越好而是越不容易错越好。我个人的经验是等Python写熟了优先级表你会自然记住大部分因为每天都在用。但作为入门阶段宁可多写括号也别去挑战复杂表达式。8. 各类型运算符实战组合从零写一个完整的判断逻辑前面把每一类运算符拆开讲了但实际项目里它们永远是组合使用的。这一节我带你走一遍一个真实小需求的完整实现把算术、比较、逻辑、成员甚至赋值运算符全部串起来。假设要写一个简单的登录校验 权限判断示例# 用户输入用户名和年龄 username input(请输入用户名: ) age int(input(请输入年龄: )) # 内置白名单用户 admin_users [admin, root, boss] vip_level 3 # 判断逻辑 if username in admin_users and age 18: print(欢迎管理员权限等级, vip_level | 0x10) elif username and age 18 and age 60: print(普通成年用户) elif not username: print(用户名不能为空) else: print(不符合访问条件) # 利用位运算给权限加一个额外标志位 perm 4 if username in admin_users else 1 perm | 2 print(f用户权限位: {perm})这个例子可能不严谨但意思很明白in做成员判断and组合多个条件or处理空值默认位运算组合权限。你实际写代码时运算符基本上都是这么嵌套使用的。我自己带新手时经常让大家做一个练习写一个程序判断某一年是否为闰年。条件是能被4整除但不能被100整除或者能被400整除。用运算符表达year int(input(请输入年份: )) if (year % 4 0 and year % 100 ! 0) or year % 400 0: print(f{year} 是闰年) else: print(f{year} 不是闰年)这个练习妙在能把算术运算符%、比较运算符、!、逻辑运算符and、or全用上而且结果客观可验证。你也可以试试自己改写成不用括号的版本看看优先级会产生什么变化然后对照想一想为什么结果不同。9. 高频踩坑与排查经验我替你们交过的学费9.1 浮点数比较永远不要用 这是Python入门头号坑。二进制浮点数的表示方式决定了我们无法精确表达所有小数0.1 0.2不等于0.3但很多人第一反应是Python的bug。其实不是这是IEEE 754浮点标准的常见现象。解决方案就是用math.isclose()或者用Decimal处理金额等对精度要求极高的场景。9.2 判断 None 用 is不要用 前面提过判断None要用x is None。在某些自定义类里可能被重载导致行为不确定。is永远比较对象身份None又是单例所以is None是最稳妥的判断方式。9.3 小心在可变对象上的行为你可能没想到在列表上和一个整数上的语义有微妙差别。对不可变对象如整数、字符串x y会创建新对象并重新绑定对可变对象如列表lst [item]等价于lst.extend([item])是原地修改。lst1 [1] lst2 lst1 lst1 [2] print(lst2) # [1, 2]lst2 也变了 lst1 [1] lst2 lst1 lst1 lst1 [2] print(lst2) # [1]lst1 重新绑定了新列表lst2 不受影响这个坑在函数传参、拷贝对象时尤其容易出问题。如果不想让原列表被修改要用切片或者copy()先复制一份。9.4 注意and/or优先级与返回值的配合逻辑运算符优先级从高到低是notandor。所以下面这个表达式flag True or False and False print(flag) # True因为先算 and再算 or如果你以为or比and优先级高就会得出错误结果(True or False) and FalseFalse。记的时候有个口诀not最优先其次and最后or和大多数语言的惯例一致。另外and/or返回值是对象本身这个特性可以用来做默认值或链式取值但新手很容易理解为返回布尔值。当你要必须拿到布尔值时可以外面套一层bool()。9.5 检查当前代码是不是被优先级坑了如果你怀疑表达式结果不对最快的方法是分步执行或加括号。你可以在IDE里把表达式拆分打印中间结果而不是一口气盯着长表达式看。很多次我以为逻辑有问题最后发现都是优先级惹的祸。10. 玩转运算符的进阶技巧与学习路线运算符看似基础但想用好也是有进阶路线的。第一个技巧善用in替代复杂的多个or判断。比如要判断字符是否属于aeiou元音字母用if ch in aeiou远比if ch a or ch e or ...清晰。判断多个值是否相等时同理if value in (1, 2, 3)。第二个技巧用and/or做条件赋值时注意可读性。比如x a or b or c可以取第一个非空值但这段代码如果别人看不明白建议加注释或者改用更显式的写法x a if a else (b if b else c)。第三个技巧想自己写支持运算符的类需要实现魔法方法。比如__add__对应__eq__对应。写一个分数类或向量类时运算符重载能极大提升代码表达力。举个例子class Vec: def __init__(self, x, y): self.x x self.y y def __add__(self, other): return Vec(self.x other.x, self.y other.y) def __repr__(self): return fVec({self.x}, {self.y}) v1 Vec(1, 2) v2 Vec(3, 4) print(v1 v2) # Vec(4, 6)当你开始写自己的类和支持运算符重载后才真正把运算符从语法规则变成设计工具。很多第三方库比如NumPy、Pandas都是靠运算符重载让你可以直接对数组做、*、比较运算而不用一个个元素循环。学习路线上我建议按这个顺序来先掌握算术、赋值、比较、逻辑这四类这是写任何逻辑都离不开的。再学成员运算符和身份运算符它们会让你的代码更Pythonic。最后学位运算符和优先级细节等你开始涉及状态标志、底层算法时再去深挖也不迟。每学一类一定要写至少三个不同场景的代码示例并把它们组合起来用。别只看文章或视频运算符这东西坐在电脑前敲一遍胜过看十遍。写在最后的一点经验我在实际教课和写代码过程中发现运算符特别容易陷入一看就会一写就废的尴尬。原因在于太基础、太琐碎很多人不屑于专门练习结果到后面写条件判断、循环、列表推导式时频繁因为优先级、短路、类型问题卡壳。如果你现在刚开始学Python建议抽出一天时间把我上面这些示例全部手敲一遍再故意改几个条件看看输出有什么变化。把错误当成学习工具比直接抄正确代码收获大得多。等到你用熟了这些运算符写起来就会很顺。Python的魅力之一就是能用最少的符号表达清晰的操作逻辑运算符正是这套表达系统最底层的零件。把地基打牢后续学函数、类、装饰器、推导式都会顺畅很多。