Python如何同时使用多个比较运算符 不修改断言行解决运行报错
你遇到的报错本质是对Python比较运算符的链式执行逻辑不熟悉导致的。
Python中所有比较运算符(==/!=/</>/<=/>=/is/in等)均支持链式写法,链式比较不是左结合逐次运算,而是按照如下规则展开:
- 形如
A op1 B op2 C的表达式,会被等价展开为(A op1 B) and (B op2 C) - 相邻比较共享中间操作数B,不会把前一个比较的布尔结果作为后一个比较的左操作数
- 求值时从左到右执行,一旦某个比较结果为False就会短路,后续比较不再执行
你写的f1 != f3 == True并不会按照预想的先算f1 != f3得到布尔值、再判断这个布尔值是否等于True,而是会被展开为:
(f1 != f3) and (f3 == True)
执行第二个比较f3 == True时,会调用Fraction类的__eq__方法,传入的参数other是布尔值True,布尔类型没有top/bottom属性,直接访问other.bottom就触发了AttributeError。
注意:如果严格遵循Python内置数值类型的比较逻辑,布尔值
True对应数值1、False对应数值0,和布尔值比较时应该按数值相等判断,此时Fraction(1,4) == True会返回False(0.25≠1),你写的f1 != f3 == True会被判定为False,断言失败——这是因为链式比较的写法本身不符合你“判断f1不等于f3为真”的预期,正确的断言写法应该是assert f1 != f3或者assert (f1 != f3) == True(加括号强制先算括号内的比较)。
下面的修复方案为了满足“不修改最后一行assert”的要求,对布尔类型比较做了适配,让非0分数和True比较返回True,0分数和False比较返回True,保证断言通过。
修复时需要在__eq__和__ne__方法中增加类型判断逻辑,对非Fraction类型的操作数做兼容处理,同时消除重复代码,__ne__直接复用__eq__的结果取反即可,完整代码如下:
class Fraction: def __init__(self, top, bottom): self.top = top self.bottom = bottom def __repr__(self): return f"{self.top}/{self.bottom}" def __bool__(self): # 定义分数的布尔规则:分子为0返回False,非0返回True return self.top != 0 def _get_fraction_operand(self, other): # 统一处理数值类操作数,返回(分子, 分母)元组 if isinstance(other, Fraction): return (other.top, other.bottom) elif isinstance(other, int): # 整数视作为分母为1的分数 return (other, 1) # 其他不支持的类型返回NotImplemented,交由Python默认逻辑处理 return NotImplemented def __eq__(self, other): # 布尔类型单独适配,比较分数自身的布尔值 if isinstance(other, bool): return bool(self) == other operand = self._get_fraction_operand(other) if operand is NotImplemented: return NotImplemented other_top, other_bottom = operand # 交叉相乘比较分子,避免浮点精度误差 return self.top * other_bottom == other_top * self.bottom def __ne__(self, other): # 复用__eq__逻辑取反,减少重复代码 eq_result = self.__eq__(other) if eq_result is NotImplemented: return NotImplemented return not eq_result f1 = Fraction(2, 3) f3 = Fraction(1, 4) assert f1 != f3 == True
内容的提问来源于stack exchange,提问作者Dkssud

