在编写自定义类时,通常需要通过==
和!=
运算符来实现等价性。在Python中,这是通过分别实现__eq__
和__ne__
特殊方法实现的。我发现最简单的方法是以下方法:
class Foo:
def __init__(self, item):
self.item = item
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.__dict__ == other.__dict__
else:
return False
def __ne__(self, other):
return not self.__eq__(other)
你知道有什么更优雅的方法吗?你知道用上面的方法比较__dict__
有什么特别的缺点吗?
注意:稍微澄清一下——当__eq__
和__ne__
未定义时,您会发现这种行为:
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
False
也就是说,a == b
的计算结果是False
,因为它确实运行a is b
,一个身份测试(即,a
与b
是同一个对象吗?)。
当定义了__eq__
和__ne__
时,您会发现这种行为(这是我们所追求的行为):
>>> a = Foo(1)
>>> b = Foo(1)
>>> a is b
False
>>> a == b
True
你所描述的就是我一直以来所做的。因为它是完全通用的,所以您总是可以将该功能分解为一个mixin类,并在需要该功能的类中继承它。
你需要小心继承:
更严格地检查类型,例如:
除此之外,你的方法也会很好,这就是特殊方法的作用。
考虑这个简单的问题:
因此,Python默认使用对象标识符进行比较操作:
重写
__eq__
函数似乎可以解决问题:在Python 2中,也要记住重写
__ne__
函数以及documentation状态:在Python 3中,由于documentation声明:
但这并不能解决我们所有的问题。让我们添加一个子类:
注意:Python 2有两种类型:
classic-style(或旧样式)类,它们不从
object
继承,声明为class A:
、class A():
或class A(B):
,其中B
是一个经典样式类;new-style类,继承自
object
,声明为class A(object)
或class A(B):
,其中B
是一个新样式的类。Python 3只有声明为class A:
、class A(object):
或class A(B):
的新样式类。对于经典样式的类,比较操作总是调用第一个操作数的方法,而对于新样式的类,它总是调用子类操作数的方法regardless of the order of the operands。
所以在这里,如果
Number
是一个典型的样式类:n1 == n3
调用n1.__eq__
n3 == n1
调用n3.__eq__
n1 != n3
调用n1.__ne__
n3 != n1
调用n3.__ne__
。如果
Number
是一个新样式的类:n1 == n3
和n3 == n1
都调用n3.__eq__
n1 != n3
和n3 != n1
都调用n3.__ne__
。要解决Python 2经典样式类的
==
和!=
运算符的不可交换性问题,当不支持操作数类型时,__eq__
和__ne__
方法应返回NotImplemented
值。documentation将NotImplemented
值定义为:在这种情况下,运算符将比较操作委托给其他操作数的反射方法。documentation将反射方法定义为:
结果如下:
如果
==
和!=
运算符的交换性在操作数为不相关类型(没有继承)时是需要的,那么返回NotImplemented
值而不是False
是正确的,即使对于新样式的类也是如此。我们到了吗?不完全是。我们有多少个唯一的号码?
集合使用对象的散列,默认情况下Python返回对象标识符的散列。让我们尝试覆盖它:
最终结果如下(我在最后添加了一些断言以供验证):
相关问题 更多 >
编程相关推荐