Python3 运算符新手实战指南WEB项目地址演示地址写在前面去年我带过一个实习生他写的代码里到处都是i i 1我说你试试他问我这俩有啥区别。后来我发现很多刚入门的同学对运算符的理解就停留在“加减乘除”这个层面遇到is和懵了看到和and更懵。这篇文章不打算讲成手册式的东西咱们就盯着新手最容易栽跟头的那些地方聊。你跟着敲一遍基本就能避开90%的坑。一、算术运算与数值计算基础先来最熟悉的。Python里的算术运算符一共七个-*///%**。前三个不用讲重点说后面四个。# 除法有三兄弟print(10/3)# 3.3333333333333335 永远返回floatprint(10//3)# 3 向下取整print(10%3)# 1 取余数# 幂运算print(2**10)# 1024新手最容易在这翻车10 / 2返回的是5.0不是5。如果你用这个结果去当列表索引当场报错。# 错误示范lst[1,2,3]idx6/3# 2.0# print(lst[idx]) # TypeError: list indices must be integers or slices, not float# 正确做法用 //idx6//3# 2print(lst[idx])# 3另外注意%在负数上的表现它跟C语言不一样print(-10%3)# 2不是-1print(10%-3)# -2规则是结果的正负跟除数一致。这个特性在做循环队列的时候其实挺有用但新手如果拿它做数学运算容易算错。二、比较判断与逻辑条件组合比较运算符返回布尔值!。这块没啥说的但有个细节Python支持链式比较这是很多语言没有的。x5# 这种写法在其他语言里可能要写成 x 3 and x 10if3x10:print(在区间内)链式比较不仅写着爽执行效率也高因为每个值只计算一次。逻辑运算符是andornot注意它们返回的不是布尔值而是最后一个被求值的操作数print(3and5)# 5print(0and5)# 0print(3or5)# 3print(0or5)# 5这个特性用好了很舒服比如nameinput(输入名字: )or匿名用户# 如果用户什么都没输name就是匿名用户但新手建议先老老实实用布尔值别花里胡哨容易把自己绕晕。三、赋值技巧与链式操作写法基础赋值没啥好说的讲几个扩展的。增量赋值-*/等等。count0count1# 等价于 count count 1但有坑。对于不可变类型整数、字符串、元组会创建新对象对于可变类型列表、字典是原地修改。# 列表的 a[1,2]ba a[3]print(b)# [1, 2, 3] b也跟着变了# 如果写成 a a [3]b不变因为创建了新列表链式赋值a b c 0三个变量指向同一个对象。对于整数没问题不可变但如果是列表就要小心了# 错误示范ab[]a.append(1)print(b)# [1] 你本来可能只想要a变# 正确做法a[]b[]拆包赋值x,y1,2x,yy,x# 交换变量不需要临时变量# 带星号的接收多余元素first,*rest[1,2,3,4]print(first)# 1print(rest)# [2, 3, 4]四、成员检测与身份验证方法成员运算符in和not in用来判断元素是否存在于序列或集合中。# 列表ifadmininuser_list:print(管理员已存在)# 字典默认检查keyifnameinuser_dict:print(user_dict[name])# 字符串ifabcinabcdef:print(包含)in背后调用的是__contains__方法不同的数据结构查找效率天差地别。列表是O(n)集合和字典是O(1)。如果你要频繁做成员检测把列表换成集合# 慢valid_ids[1,2,3,4,5,...]# 假设有10000个ifidinvalid_ids:# 平均要查5000次# 快valid_ids{1,2,3,4,5,...}ifidinvalid_ids:# 平均1次身份运算符is和is not比较的是内存地址比较的是值。这是新手重灾区。a[1,2,3]b[1,2,3]print(ab)# True内容一样print(aisb)# False不是同一个对象caprint(cisa)# True指向同一个对象对于小整数-5到256Python做了缓存所以x256y256print(xisy)# True因为缓存了x257y257print(xisy)# False没缓存每次新建经验法则判断值是否相等用判断是不是同一个对象用is。判断None永远用is None。五、位运算原理及实际应用场景位运算符|^~。新手看到位运算就发怵但其实没那么可怕。先把十进制转成二进制看就清楚了a0b1010# 10b0b1100# 12print(ab)# 0b1000 (8) 按位与都是1才为1print(a|b)# 0b1110 (14) 按位或有1就为1print(a^b)# 0b0110 (6) 按位异或不同为1print(~a)# -11 按位取反print(a1)# 0b10100 (20) 左移一位 乘以2print(a1)# 0b0101 (5) 右移一位 除以2实际用到的地方权限控制最经典READ1# 0b001WRITE2# 0b010EXEC4# 0b100permissionREAD|WRITE# 0b011 3ifpermissionREAD:# 判断是否有读权限print(可以读)状态标志位用一个整数存多个开关状态省内存。快速乘除2的幂x n比x * 2**n快得多虽然现在编译器优化后差距不大了。颜色处理RGBA颜色拆通道color0xFF336688r(color24)0xFF# 255g(color16)0xFF# 51b(color8)0xFF# 102acolor0xFF# 136日常业务开发可能用不到但在写底层库、网络协议解析、算法优化的时候位运算是绕不开的。六、运算符优先级与括号控制Python的运算符优先级从高到低大致是()括号**幂运算x-x~x正负号、按位取反*///%-^|!isinnotandor但是没人会去背这个表。我写了好几年Python也只记住几个容易搞混的# 容易翻车的ifa1andb2:# 没问题ifa1b2:# 完蛋的优先级高于变成了 a (1 b) 2还有这个经典陷阱# 你想判断 a 不是 0 且不是 Noneifnota0andaisnotNone:# 实际解释为 (not a 0) and (a is not None)# 没问题但读起来费劲# 更清晰的写法ifa!0andaisnotNone:我的建议是除了加减乘除和比较其他情况一律加括号。多写几个括号不丢人丢人的是括号写错了找半天bug。# 推荐result(ab)*(c-d)flag(x0)and(y10)or(z0)七、综合案例构建简易计算器把前面学的东西串起来写个命令行计算器。importoperatorimportre# 操作符映射到对应的函数OPERATORS{:operator.add,-:operator.sub,*:operator.mul,/:operator.truediv,//:operator.floordiv,%:operator.mod,**:operator.pow,}defcalculate(expr):支持加减乘除、幂、取余、取整也支持括号# 去除空格exprexpr.replace( ,)# 先处理括号简易版只支持一层括号while(inexpr:startexpr.rfind(()# 从最内层开始endexpr.find(),start)ifend-1:raiseValueError(括号不匹配)innerexpr[start1:end]result_calc_no_parentheses(inner)exprexpr[:start]str(result)expr[end1:]return_calc_no_parentheses(expr)def_calc_no_parentheses(expr):计算不含括号的表达式# 处理幂运算右结合得从右往左partsexpr.split(**)iflen(parts)1:# 简单处理只支持两个数的幂# 实际上还需要处理更复杂的这里演示用left_calc_mul_div(parts[0])right_calc_mul_div(parts[1])returnleft**rightreturn_calc_mul_div(expr)def_calc_mul_div(expr):处理乘除取余取整# 先按加减法分割分别计算乘除部分# 用正则分割保留操作符tokensre.split(r([-]),expr)result0current_opfortokenintokens:iftokenin-:current_optokencontinueiftoken:continueval_calc_mul_div_single(token)ifcurrent_op:resultvalelse:result-valreturnresultdef_calc_mul_div_single(expr):计算单个乘除项# 按 */ // % 分割# 这里简化实现只演示思路# 实际完整实现需要处理优先级和左结合if//inexpr:left,rightexpr.split(//)returnfloat(left)//float(right)if%inexpr:left,rightexpr.split(%)returnfloat(left)%float(right)if*inexpr:left,rightexpr.split(*)returnfloat(left)*float(right)if/inexpr:left,rightexpr.split(/)returnfloat(left)/float(right)returnfloat(expr)# 使用if__name____main__:whileTrue:try:exprinput( )ifexpr.lower()in(q,quit,exit):breakresultcalculate(expr)print(f{result})exceptExceptionase:print(f出错了:{e})这个计算器很简陋但已经用到了算术、比较判断操作符、赋值、成员检测in等知识点。你可以试着完善它比如支持更多层括号、支持负数等。八、常见类型错误与排查思路新手用运算符最常见的几类错误1. TypeError: unsupported operand type(s)# 字符串和数字不能相加print(11)# TypeError# 修复print(int(1)1)# 2print(1str(1))# 112. TypeError: can’t multiply sequence by non-intprint(abc*3)# abcabcabc 这个可以print(abc*1.5)# TypeError 不能乘浮点数3. 字典的 in 只检查 keyd{name:张三,age:18}print(张三ind)# False检查的是key不是valueprint(nameind)# True4. 列表和元组傻傻分不清# 以为 (1) 是元组其实是整数t(1)print(type(t))# class int# 正确写法t(1,)print(type(t))# class tuple排查思路看到TypeError先打印出变量的类型print(type(x))看到ValueError打印出变量的值print(x)不确定运算符行为的时候在交互式环境python shell里快速测试九、浮点数精度陷阱与规避方案这是个老生常谈但永远有人踩的坑。print(0.10.2)# 0.30000000000000004print(0.30.10.2)# False原因0.1 在二进制里是无限循环小数计算机只能近似存储。规避方案方案一用round()控制精度result0.10.2ifround(result,10)0.3:print(相等)方案二用decimal.Decimal做精确计算fromdecimalimportDecimalprint(Decimal(0.1)Decimal(0.2))# 0.3# 注意一定要传字符串传浮点数等于没传方案三比较时用误差范围defis_close(a,b,eps1e-9):returnabs(a-b)epsprint(is_close(0.10.2,0.3))# True金额计算千万别用浮点数用Decimal或者以分为单位存整数。十、代码规范与可读性优化建议运算符相关的代码规范照着 PEP 8 来就行1. 二元运算符两边加空格# 好x1yx2# 不好x1yx22. 优先级高的运算符可以不加空格# 可以resulta*bc*d# 但更推荐统一加空格更清晰resulta*bc*d3. 等号在关键字参数和默认值两边不加空格# 好deffunc(name,age18):pass# 不好deffunc(name,age18):pass4. 比较运算连写时保持一致性# 好ifx0andx100:pass# 更好利用链式比较if0x100:pass5. 避免过长的链式调用# 容易看晕ifa1andb2orc3andd4ore5:pass# 拆开或者加括号cond1a1andb2cond2c3andd4ifcond1orcond2ore5:pass6. 用in替代多个or比较# 不好ifx1orx2orx3orx4:pass# 好ifxin(1,2,3,4):pass最后说两句运算符是Python最基础的东西但基础不等于简单。我见过写了两年Python的人还在用type(x) int而不是isinstance(x, int)也见过到处用拼字符串而不是用join。这篇文章覆盖了新手最容易遇到的问题但光看没用你得打开编辑器一个个敲一遍。特别是位运算和浮点数那两块自己改改数值看看输出印象会深很多。如果遇到文章里没讲到的坑欢迎随时交流。写代码的路上谁还没被运算符坑过几回呢。