经常有学员跑来问我“老师Python入门语法我都看完了怎么一写作业就卡壳”我问他们卡在哪十有八九都出在操作符上。不是不知道是加、-是减而是一道题里混着算术、比较、逻辑运算时整个人就懵了。所以我干脆整理了一套以操作符为主线的测验题一边做题一边查漏补缺比干看书管用得多。这份测验面向刚学完Python基础语法、正准备往流程控制或函数方向进阶的初学者也适合用来做教学后的随堂检测。我的目标很直接把Python里最常用的几类操作符全部过一遍用题目逼着你暴露问题再逐题拆解背后的原理。如果你能不看答案把这套题做对八成以上说明操作符这一关你真的过了如果做错不少那正好这篇文章就是给你准备的。1. 为什么专门把“操作符”拎出来做测验很多人觉得操作符不就是加减乘除吗有什么好测的这种想法害人不浅。我见过太多人在变量赋值、函数调用上都挺顺一写条件判断就翻车归根结底是对操作符的理解停留在“认识符号”的层面而不是“理解运算规则”的层面。1.1 操作符是Python里最容易被“想当然”的部分Python的操作符看着简单实际规则相当多。比如/和//的区别和的区别and和的区别这些如果没搞清楚写代码就是凭感觉。更麻烦的是Python有一些看似违背直觉的规则比如链式比较1 2 3居然可以直接这么写比如not的优先级其实比and和or都低比如2 ** 3 ** 2算出来是 512 而不是 64。这些规则不是靠背能记住的必须通过做题、出错、纠正才能形成条件反射。我设计这套测验的初衷就是让学习者在一个安全的环境里先把错犯个遍免得日后在真实项目里踩雷。1.2 一份测验题应该怎么设计才对得起读者既然叫“测验”就不能随便堆几道题了事。我设计这套题目时给自己定了三个标准覆盖全面算术、比较、逻辑、赋值、成员、身份、位运算这几大类都得有不能只盯着算术运算符。有梯度从直接考察运算结果的简单题到混合运算的进阶题再到需要理解底层机制的拔高题逐步加码。每道题都有“坑”所谓“坑”不是故意刁难人而是真实编程中确实容易出错的地方。比如把写成、忽略了短路求值、误以为is和等价——这些都是我实际看到学员犯过的错。这套题的适用场景不限于自学自测。你要是老师可以把它当作课堂小测你要是团队里带新人的也可以拿它当面试前的热身题。做完一遍基本就能看出一个人对Python操作符的掌握程度了。2. 先过一遍核心规则Python操作符的分类与优先级如果你对操作符还只是混个脸熟建议先把这一章过一遍再去做题。这里我不会把官方文档搬过来而是挑最实用、最容易出问题的点讲清楚。这相当于先给你一张地图再做测验就不会两眼一抹黑。2.1 Python里到底有哪些操作符分别管什么为了方便对照我把Python里最常用的操作符按类别整理成了表格。这个表格的特点是按“干的事”来分而不是按符号长相来分这样更贴近实际使用场景。类别操作符典型用途算术操作符-*///%**数值计算、字符串拼接、序列重复比较操作符!比较两个值的大小或相等性赋值操作符-*///%**给变量赋值或原地更新逻辑操作符andornot组合多个布尔条件成员操作符innot in判断元素是否在序列或集合中身份操作符isis not判断两个引用是否指向同一对象位操作符^ ~ 海象操作符:在表达式中同时完成赋值和返回这里我想多说一句最容易被人忽略的是海象操作符:。它是Python 3.8才引入的很多人学过但没实际用过。它的作用是在表达式内部给变量赋值比如# 普通写法 data input(请输入) if len(data) 3: print(f长度是{len(data)}) # 海象写法 if (data : input(请输入)) and len(data) 3: print(f长度是{len(data)})你的代码里不一定非得用它但看别人的代码时至少要认识它。2.2 优先级和结合性决定表达式怎么算的关键操作符优先级是个老生常谈的话题但每次考倒一片人。Python的优先级从高到低大致是幂运算**正负号x-x一元运算符算术运算*///%算术运算-比较运算!not逻辑非and逻辑与or逻辑或赋值运算等这里有两个特别容易搞错的点。第一**的结合性是右结合。也就是说2 ** 3 ** 2的运算顺序是从右往左的先算3 ** 2 9再算2 ** 9 512。很多人想当然地以为从左往右算结果得到 64。这就是典型的“认识符号但不认识规则”。第二not的优先级比and和or低。这意味着not a b实际上先算a b再对结果取反等价于not (a b)。而not a and b等价于(not a) and b。有些初学者看到not就以为它优先管住后面所有东西这是一个隐蔽的坑。遇到复杂的表达式我的建议很简单拿不准就加括号。显式括号不会降低性能却能极大提升可读性也能避免自己和别人理解偏差。测验里我会特意出几道不写括号的题就是要看你有没有这个意识。2.3 短路求值逻辑操作符最容易被忽略的特性and和or在Python里不只是返回布尔值这么简单它们还有一个关键特性——短路求值。a and b如果a为假直接返回a不再计算b。a or b如果a为真直接返回a不再计算b。如果没触发短路则返回最后一个被计算的值。这个特性在实战中特别有用。比如你想安全地获取一个可能为空的对象属性可以写result data and data.get(name)如果data是假值比如None或空列表直接返回data不会去调用.get()方法也就不会报AttributeError。但短路求值也可能带来隐蔽的bug。看这个例子def check(): print(check被调用了) return True print(False and check()) # 输出 Falsecheck() 根本没执行如果你在and右边放了一个函数调用而这个函数有副作用比如写入日志、修改全局变量短路发生时就白白错过了这次调用。测验里我会专门设计一题来考察这一点因为这是真实项目里最容易出幺蛾子的地方。3. 测验题逐题拆解从基础到易错全覆盖下面进入正题。我按难度递增的顺序把整套测验的核心题目一个个摆出来每题都给出答案、解析和常见错误分析。强烈建议你先自己动手在Python交互环境或IDE里敲一遍再看解析效果完全不同。3.1 第一梯队算术操作符的“分水岭”题目题目1print(7 / 2) print(7 // 2) print(7 % 2) print(2 ** 3)这道题本质是考察四个最常用算术运算符的区别。答案是3.5 3 1 8第一个7 / 2是真正的除法结果是浮点数3.5这是Python 3里最常见的“坑”。很多从Python 2转过来的人或者从C语言、Java转过来的人总会下意识以为两个整数相除结果是整数但Python 3里/永远返回浮点数。如果你想要整数结果必须用//。第二个7 // 2是地板除结果为3。注意地板除是向下取整不是截断取整。所以-7 // 2的结果是-4而不是-3因为-3.5向下取整是-4。这一点极其容易踩坑我在教负数运算时强调过无数次还是有人错。第三个7 % 2是取余结果为1这个通常没什么争议。第四个2 ** 3是幂运算结果为8。注意**的优先级比一元负号高所以-2 ** 2的结果是-4而不是4。你要是想让结果为正的4必须写(-2) ** 2。这道题看似简单实际上包含了两个隐藏考点。题目2print(10 / 3) print(10 // 3) print(10 % 3) print(5 // 2) print(-5 // 2) print(5 % -2)这是上一题的升级版专门考察负数场景下的地板除和取余行为。答案是3.3333333333333335 3 1 2 -3 -1这里我最想强调两点。第一10 / 3的结果在Python里显示为3.3333333333333335后面那个5不是Bug而是浮点数二进制存储的精度误差。你要是拿它和10 / 3 3.3333333333333333比较结果可能是False。所以浮点数之间做相等比较前一定三思。第二5 % -2的结果是-1而不是1。Python里取余运算的结果符号总是跟随除数也就是第二个操作数的符号。所以5 % -2除数是负数结果也是负数为-1。-5 % 2除数是正数结果也是正数为1。这个规则跟C语言不一样很多人第一次遇到直接懵。理解了“余数符号跟随除数”之后你在处理循环索引、时间换算、周期性问题时都会更清楚。3.2 第二梯队比较与逻辑操作符的“短路陷阱”题目3a 5 b 10 print(a 3 and b 20) print(a 3 or b 20) print(not a 3) print(1 a 10)答案依次是True、True、False、True。前三个常规考察and、or、not只要记住各自的真值表基本没问题。真正有意思的是第四个1 a 10。这是Python特有的链式比较它等价于1 a and a 10结果为True。这种写法在数学上很自然但在很多语言里是语法错误。Python允许你这么写而且性能上也不差所以放心用。不过要注意链式比较有个隐藏特性中间的表达式只会被求值一次。比如def get_val(): print(get_val被调用) return 5 print(1 get_val() 10)这里get_val()只会被调用一次。如果换成1 get_val() and get_val() 10它会被调用两次。虽然大多数场景下这个区别无伤大雅但如果函数有副作用行为就会不同。题目4print(0 and 100) print(2 and 3) print(0 or 100) print(2 or 3) print([] or default) print( and value)这题不考布尔值考的是and和or的返回值本质。答案0 3 100 2 default 第一行0 and 100因为0是假值触发短路直接返回0根本不看100。第二行2 and 32是真值继续往右算返回3。注意这里返回的是3而不是True。第三行0 or 1000是假值继续往右算返回100。第四行2 or 32是真值触发短路直接返回2。后面两行是实战中非常常见的“默认值”模式。[] or default返回default因为空列表是假值 and value返回因为空字符串是假值且触发短路。很多初学者以为and和or返回的一定是布尔值这是不对的。Python里的and和or返回的是最后参与运算的操作数而不一定是True或False。这个特性让代码更灵活但也让不熟悉的人容易踩坑。3.3 第三梯队身份操作符is到底在比什么题目5a [1, 2, 3] b [1, 2, 3] c a print(a b) print(a is b) print(a is c)答案依次是True、False、True。a b比较的是值两个列表内容一样所以是True。a is b比较的是身份也就是内存地址a和b是两个不同的列表对象即使内容一模一样地址也不同所以是False。a is c为True因为c a只是让c指向a同一个对象并没有创建新列表。这道题是新手重灾区。很多人以为is和差不多其实差别巨大。用一句话概括比“值是否相等”is比“是不是同一个东西”。更隐蔽的坑在整数和字符串上x 256 y 256 print(x is y) # True m 257 n 257 print(m is n) # 可能是 FalseCPython里有一个“小整数缓存”机制-5 到 256 之间的整数是预先创建好的对象所以x is y为True。但超过这个范围Python每次都会新建对象m is n就可能是False。在真实开发里千万不要用is来判断整数或字符串是否相等这个行为在不同版本、不同实现下都可能变老老实实用最安全。3.4 第四梯队类型转换与操作符的“混搭”题目6print(3 * 3) print(3 3) print(int(3) 3) print(str(3) str(3))第一个3 * 3结果是333。字符串和整数做乘法表示重复字符串。第二个3 3直接报TypeError。字符串不能和整数相加Python不会隐式帮你做类型转换。第三个int(3) 3结果是6。显式转换后再相加没问题。第四个str(3) str(3)结果是33。把数字转成字符串再拼接没问题。这道题考察的核心是Python是强类型语言不会在你不注意的时候偷偷帮你转换类型。相比之下JavaScript里3 3会得到33这让很多人转到Python时极其不适应。但更有意思的是下面这行代码print(True 1) # 2因为bool是int的子类True在数值运算中会被当成1。所以True 1等于2。这个特性偶尔会派上用场但大多数时候属于“知道就行别乱用”的范畴。3.5 第五梯队成员操作符与赋值操作符的实战变形题目7fruits [apple, banana, cherry] print(apple in fruits) print(grape not in fruits) print(a in apple) print(3 in range(5))答案True、True、True、True。前面三个都好理解in可以判断元素是否在列表里、字符串里。第四个值得单独提一下3 in range(5)返回True因为range(5)生成的是0到4的整数序列3在这个序列里。但注意range判断成员时是通过数学计算直接判断的不会真的把所有元素列出来所以即使range(10**10)这种超大范围判断速度也很快。这是Python内部对range做的优化。题目8x 10 x 5 print(x) x * 2 print(x) x // 3 print(x)答案15、30、10。、*、//这类复合赋值操作符本质是“先计算右边的表达式再对当前变量做对应操作最后把结果赋回变量”。但有一个细节需要注意x 1对不可变对象如整数、字符串、元组是重新绑定变量名对可变对象如列表则可能是在原对象上修改。比如a [1, 2] b a a [3] print(b) # [1, 2, 3]b也跟着变了因为a [3]实际上调用的是a.__iadd__方法对列表来说就是就地修改b和a指向同一个列表所以b的内容也变了。如果你写的是a a [3]则是新建列表再赋值b不会变。这两者的区别我在带项目时见过不止一次因为混淆它们而引发的bug。3.6 第六梯队位操作符的几个实用场景题目9print(5 3) print(5 | 3) print(5 ^ 3) print(~5) print(1 3) print(8 2)答案1 7 6 -6 8 2位操作符在日常业务代码里用得确实不多但在权限系统、状态标志位、性能敏感的底层计算里却非常有用。先手算验证一下5 的二进制是1013 的二进制是011。5 3按位与101 011 001十进制是1。5 | 3按位或101 | 011 111十进制是7。5 ^ 3按位异或101 ^ 011 110十进制是6。~5按位取反在Python里结果不是我们直觉上的-5而是-6。因为Python的整数是任意精度的补码表示~x等价于-x - 1所以~5是-6。1 3左移3位1变成1000十进制是8相当于乘以2**3。8 2右移2位1000变成10十进制是2相当于整除2**2。位运算的一个经典应用是“用整数来存储多个布尔标志位”。比如一个文件权限系统用r、w、x三个位来表示读、写、执行权限READ 4 # 100 WRITE 2 # 010 EXECUTE 1 # 001 permission READ | WRITE # 6表示可读可写 if permission READ: print(可读) if permission EXECUTE: print(可执行) # 这行不会输出这种写法的好处是节省内存、可以用一个整数同时表示多个开关状态在很多算法题和底层库里都能看到。测验里出这道题的目的不是让你背答案而是让你知道Python里有这么一类操作符将来遇到了能反应过来。3.7 第七梯队混合运算的“终极考验”题目10print(2 3 * 4 ** 2) print((2 3) * 4 ** 2) print(not True and False or True) print(5 5 and 6 ! 7 or 8 3)答案50 80 True True第一行4 ** 2 16然后3 * 16 48最后2 48 50。第二行加了括号后先算2 3 5再算4 ** 2 16最后5 * 16 80。第三行not True是False因此False and False触发短路直接返回False接着False or True返回True。整个过程等价于(not True) and False or True。第四行5 5是True6 ! 7是True所以True and True为True再True or 8 3因为短路直接返回True。注意这里8 3根本不会被计算。这类混合运算题就是检验你优先级掌握程度的试金石。如果你能在5秒内说出正确答案说明你已经基本形成了操作符的“肌肉记忆”。4. 做完测验后的深度复盘这些“坑”值得反复看做题不是目的把错的题弄懂才是目的。下面这些坑是我在带教学、改作业、看代码评审时反复遇到的这里一次性列出来。4.1 赋值 vs 比较一个等号引发的血案Python里是赋值是相等比较。这个区别几乎每个教程都会讲但实际写代码时还是有人把if a 5:当成“如果a等于5”结果直接报语法错误。更隐蔽的是在循环或条件里不小心多写了一个等号虽然不报错但逻辑完全错了a 5 if a 5: # 语法错误 print(相等)正确写法是a 5 if a 5: # 注意两个等号 print(相等)如果实在怕记混有个土办法读代码时把读成“被赋值为”把读成“等于”一读就知道哪里不对了。还有个小技巧Python 3.8之后如果你想在条件里“随手赋值并比较”可以用海象操作符if (n : len(data)) 10: print(f长度是{n}超过10了)这既避免了len(data)调用两次也绕开了“赋值还是比较”的混淆。4.2 浮点数比较绝对不要直接我在前面的题目里已经铺垫过浮点数的精度问题这里再往深里说一层。你可能会问“既然浮点数有精度问题那我到底该怎么比较两个浮点数”常规做法是设置一个很小的容差值epsilona 0.1 0.2 b 0.3 print(abs(a - b) 1e-9) # True或者用Python 3.5 的math.iscloseimport math print(math.isclose(0.1 0.2, 0.3)) # Truemath.isclose是官方推荐的方式默认考虑相对误差和绝对误差写起来也最清晰。测验里如果出了0.1 0.2 0.3这种题可得留个心眼千万不要直接写True。4.3 优先级记不全怎么办括号是最诚实的工具坦白说哪怕是写了好几年Python的人也很难把每个操作符的优先级顺序倒背如流。我的做法是除了最基础的算术运算其他一律加括号。这无关技术能力而是代码可读性的问题。举个例子result a and b or c and d这种写法也许逻辑上是正确的但读者需要花时间解析优先级。倒不如写成result (a and b) or (c and d)阅读成本瞬间降低。操作符测试里那些“不写括号考你优先级”的题是为了帮你建立对规则的理解但真正写项目代码时可读性优先括号不是示弱而是专业素养。4.4 布尔值是整数的子类你以为的“类型错误”其实不是我在题目6的解析里提到True 1结果是2。这个特性偶尔会坑人。比如你想统计一个列表里大于0的元素个数可能会顺手写出count sum(x 0 for x in nums)这里x 0返回布尔值而sum求和时把True当成1、False当成0所以这行代码能正常工作。但如果你在if条件里依赖这种隐式转换就要小心了if 2: print(2是真理值) if 0: print(这行不会输出)Python所有的对象都可以直接用在布尔上下文中判断标准是“真假值”truthiness而不是“是否等于True”。空列表、空字符串、0、None 都是假值其余大多是真值。理解了这个很多看似奇怪的代码都能看懂了。4.5is和的终极区分一句话记牢你想比“值”就用你想比“是不是同一个对象”就用is。除了一种特殊情况你想判断一个变量是不是None。这时官方推荐用is None而不是 Noneif data is None: print(data是None)因为None在Python里是单例对象所有None都是同一个对象用is判断既快又安全。但如果你要用判断其他两个对象是否相等那完全没有问题。4.6 复合赋值操作符和可变对象的隐藏风险我在题目8的解析里提到了a [3]和a a [3]的区别。这个知识点在函数参数传递时尤其重要。def add_item(lst, item): lst [item] # 外部列表会被修改 def add_item_safe(lst, item): lst lst [item] # 外部列表不会被修改 return lst my_list [1, 2] add_item(my_list, 3) print(my_list) # [1, 2, 3] my_list2 [1, 2] new_list add_item_safe(my_list2, 3) print(my_list2) # [1, 2] print(new_list) # [1, 2, 3]如果你在函数里用了且操作的是可变对象外部变量可能被悄悄改动这常常导致非常难排查的bug。要记住一个原则复合赋值操作符对不可变对象是“创建新值”对可变对象是“就地修改”。如果你拿不准当前对象是可变还是不可变先查一下id或者干脆用显式的赋值。5. 从测验到实战操作符在真实场景里的正确打开方式做对几道测验题不代表你就能写出好代码从测验到实战之间还差着“场景思维”这道坎。这一章我会把操作符放到真实的应用场景里告诉你它们是怎么协同工作的。5.1 条件判断中的操作符组合用括号理清业务逻辑假设你在写一个用户权限校验的功能要求是“管理员或者VIP用户都能进入但被封禁的用户除外”。这个逻辑直接用自然语言描述很简单翻译成代码就讲究了if (is_admin or is_vip) and not is_banned: print(允许进入) else: print(拒绝进入)这里and、or、not三个操作符全用上了。最关键的写法是(is_admin or is_vip)这个括号必须加。如果不加括号写成is_admin or is_vip and not is_banned因为and优先级高于or实际等价于is_admin or (is_vip and not is_banned)。这样当is_admin为True时即使is_banned为True也会放行逻辑就错了。业务需求越复杂括号越能体现价值。我见过不少老手在代码评审时对着一串andornot盯半天最后加上括号长舒一口气。不要和优先级较劲把优先级交给括号把可读性留给读者。5.2 用操作符简化数据处理从笨重到优雅再看一个数据处理场景。你想筛选出一个列表中所有“非空字符串且长度大于3”的元素最直观的写法items [abc, , hello, a, world] result [] for item in items: if item and len(item) 3: result.append(item) print(result) # [hello, world]这里item直接用在了布尔上下文中。空字符串是假值所以item and len(item) 3在item为空时直接返回空字符串不会去调用len()既简洁又安全。同样的思路可以用在默认值设置上name input(请输入名字) or 匿名用户如果用户什么都没输入name就是空字符串属于假值于是or返回匿名用户。这是Python里非常经典的“默认值”写法一行代码替代了三行if-else。5.3 在循环和索引中善用算术操作符另一个高频场景是循环索引。比如你想每隔两个元素处理一次列表data [a, b, c, d, e, f] for i, value in enumerate(data): if i % 2 0: print(f处理偶数索引{i}{value})%取余操作符在这里就是“判断是否间隔元素”的利器。再比如轮询通知、分页计算、循环队列也全靠%和//配合。一个实用小技巧如果你要把秒数换算成“时:分:秒”格式可以用total 3661 hours total // 3600 minutes (total % 3600) // 60 seconds total % 60 print(f{hours}:{minutes}:{seconds}) # 1:1:1//负责取整部分%负责剩余部分两个操作符配合得天衣无缝。5.4 海象操作符在实战中的正确姿势海象操作符:我前面提过这里给一个更“实用主义”的例子。比如你要从文件里逐行读取直到遇到空行with open(data.txt, r) as f: while (line : f.readline().strip()): print(f处理行{line})如果不使用海象操作符你得先读一行、判断是否为空、然后再进入循环体处理过程会比较绕with open(data.txt, r) as f: line f.readline().strip() while line ! : print(f处理行{line}) line f.readline().strip()两种写法都对但第一种明显更紧凑而且不会出现“循环体内忘了更新行变量”的低级错误。海象操作符用得好确实能让代码更接近你的思维过程。6. 自查清单你的操作符掌握度到底什么水平讲完题目和实战最后给出一份自查清单。你可以对照清单在脑子里逐条过一遍看看哪条还不够熟练。检查项熟练标准自查结果算术操作符 - * / // % **能准确说出每个操作符的用途知道/返回浮点数、//向下取整、%符号跟随除数是 / 否赋值操作符合复合赋值能区分和理解对可变对象和不可变对象的影响是 / 否比较操作符理解比较值、is比较身份能用math.isclose处理浮点数比较是 / 否逻辑操作符理解andor的短路求值和返回值规则知道not的优先级低于andor是 / 否成员操作符innot in能判断元素是否在列表、字符串、字典、set中知道判断字典时默认查键是 / 否位操作符至少理解^ ~ 的含义能看懂权限标志位代码操作符优先级能在混合表达式中正确判断运算顺序遇到复杂表达式主动加括号是 / 否我再补充一点容易被忽略的字典的in判断的是键而不是值。d {name: 张三, age: 18} print(name in d) # True键存在 print(张三 in d) # False值不在键里这个细节很基础但我见过不少面试者在这个问题上翻车。你如果觉得自己的操作符基础还不够牢把这份清单打印出来贴在工位旁边边写代码边对照进步会非常快。我从一开始教Python时就坚信一个理念语法可以查文档但操作符的直觉必须靠练。文档只能告诉你%是取余但只有当你真正手算过-7 % 3并意识到结果是2时你才真正理解了它。这套测验就是帮你建立这种直觉的捷径。把上面的题一道一道亲手敲一遍、错一遍、再改一遍比你囫囵吞枣刷十遍教程都管用。操作符这一关过了后面的条件判断、循环、函数、类都会顺畅很多。