从Python的早期版本开始,就允许对象之间进行比较。为了描述您的对象何为“更大”或“相等”,创建了特殊的比较方法(eq,ne,lt,le,gt,ge)。PEP 207(Python 2.1+)的出现实现了统一的比较协议,使程序员能够明确控制比较时的行为。
标准类型(int,str)是根据“值”进行比较的,但用户自定义类默认是根据“身份”(is)进行比较的。如果没有实现或错误实现魔术比较方法,那么您的对象在处理集合、排序、使用set和dict时,将导致意外的行为,有时甚至会导致类型错误。
必须在您的类中明确地定义比较方法。通常实现__eq__(相等)和__lt__(小于),其余方法可以通过使用装饰器functools.total_ordering自动获得。
示例代码:
from functools import total_ordering @total_ordering class Point: def __init__(self, x, y): self.x = x self.y = y def __eq__(self, other): if not isinstance(other, Point): return NotImplemented return self.x == other.x and self.y == other.y def __lt__(self, other): if not isinstance(other, Point): return NotImplemented return (self.x, self.y) < (other.x, other.y) # 使用: p1 = Point(1, 2) p2 = Point(1, 3) print(p1 < p2) # True print(p1 == p2) # False
关键特点:
仅实现一个比较方法(eq__或__lt)可以使一切正常工作吗?
不可以。仅实现__eq__,您将确保正确的相等比较,但运算符 <, >, <=, >= 会表现出标准行为(通常通过对象身份比较)。为了达到完整的行为,应该实现足够的方法,或者使用functools.total_ordering。
为什么Python中没有像其他语言那样的compare(x, y)函数?
Python继承了为每个操作符提供特殊“魔术”方法的概念,从Python 3开始,cmp函数被移除。对于对象的通用比较,使用丰富的比较方法(lt,__eq__等),对于排序,通过sort/sorted函数的key参数来实现。
如果比较一个对象与一个没有实现所需方法的类型,会发生什么?
如果在__eq__或其他方法中针对不支持的类型返回NotImplemented,Python将自动尝试在第二个操作数上调用相反的方法或返回False。如果不处理这种情况,可能会导致TypeError或非常不正确的行为。
示例代码:
class Foo: def __eq__(self, other): return NotImplemented class Bar: pass print(Foo() == Bar()) # False(尝试通过Bar.__eq__)
开发者创建了一个User类,只实现了__eq__而没有__hash__,使得用户根据电子邮件被判断为相等。然后尝试在set中使用User对象/作为字典的键。
优点:
缺点:
程序员使用@total_ordering并实现了__eq__和__lt__,并且进行类型检查,若类是不可变的,还添加了__hash__。对象可以正确比较、排序,并且可以作为字典的键。
优点:
缺点: