A hashable class has an __eq__
method, and a __hash__
method that agrees with equality.
+When a hash method is defined, an equality method should also be defined; otherwise object identity is used for equality comparisons
+which may not be intended.
+
Note that defining an __eq__
method without defining a __hash__
method automatically makes the class unhashable in Python 3.
+(even if a superclass defines a hash method).
+If a __hash__
method is defined, ensure a compatible __eq__
method is also defined.
+
+To explicitly declare a class as unhashable, set __hash__ = None
, rather than defining a __hash__
method that always
+raises an exception. Otherwise, the class would be incorrectly identified as hashable by an isinstance(obj, collections.abc.Hashable)
call.
+
In the following example, the A
class defines an hash method but
+no equality method. Equality will be determined by object identity, which may not be the expected behaviour.
+
In order to ensure the ==
and !=
operators behave consistently as expected (i.e. they should be negations of each other), care should be taken when implementing the
+__eq__
and __ne__
special methods.
In Python 3, if the __eq__
method is defined in a class while the __ne__
is not,
+then the !=
operator will automatically delegate to the __eq__
method in the expected way.
+
However, if the __ne__
method is defined without a corresponding __eq__
method,
+ the ==
operator will still default to object identity (equivalent to the is
operator), while the !=
+ operator will use the __ne__
method, which may be inconsistent.
+
Additionally, if the __ne__
method is defined on a superclass, and the subclass defines its own __eq__
method without overriding
+the superclass __ne__
method, the !=
operator will use this superclass __ne__
method, rather than automatically delegating
+to __eq__
, which may be incorrect.
+
Ensure that when an __ne__
method is defined, the __eq__
method is also defined, and their results are consistent.
+In most cases, the __ne__
method does not need to be defined at all, as the default behavior is to delegate to __eq__
and negate the result.
In the following example, A
defines a __ne__
method, but not an __eq__
method.
+This leads to inconsistent results between equality and inequality operators.
+
In the following example, C
defines an __eq__
method, but its __ne__
implementation is inherited from B
,
+which is not consistent with the equality operation.
+
A class that implements the rich comparison operators
+(__lt__
, __gt__
, __le__
, or __ge__
) should ensure that all four
+comparison operations <
, <=
, >
, and >=
function as expected, consistent
+with expected mathematical rules.
+In Python 3, this is ensured by implementing one of __lt__
or __gt__
, and one of __le__
or __ge__
.
+If the ordering is not consistent with default equality, then __eq__
should also be implemented.
+
Ensure that at least one of __lt__
or __gt__
and at least one of __le__
or __ge__
is defined.
+
+The functools.total_ordering
class decorator can be used to automatically implement all four comparison methods from a
+single one,
+which is typically the cleanest way to ensure all necessary comparison methods are implemented consistently.
In the following example, only the __lt__
operator has been implemented, which would lead to unexpected
+errors if the <=
or >=
operators are used on A
instances.
+The __le__
method should also be defined, as well as __eq__
in this case.
In order to conform to the object model, classes that define their own equality method should also
-define their own hash method, or be unhashable. If the hash method is not defined then the hash
of the
-super class is used. This is unlikely to result in the expected behavior.
A class can be made unhashable by setting its __hash__
attribute to None
.
In Python 3, if you define a class-level equality method and omit a __hash__
method
-then the class is automatically marked as unhashable.
When you define an __eq__
method for a class, remember to implement a __hash__
method or set
-__hash__ = None
.
In the following example the Point
class defines an equality method but
-no hash method. If hash is called on this class then the hash method defined for object
-is used. This is unlikely to give the required behavior. The PointUpdated
class
-is better as it defines both an equality and a hash method.
-If Point
was not to be used in dict
s or set
s, then it could be defined as
-UnhashablePoint
below.
-
-To comply fully with the object model this class should also define an inequality method (identified -by a separate rule).
- -In order to conform to the object model, classes should define either no equality methods, or both
-an equality and an inequality method. If only one of __eq__
or __ne__
is
-defined then the method from the super class is used. This is unlikely to result in the expected
-behavior.
When you define an equality or an inequality method for a class, remember to implement both an
-__eq__
method and an __ne__
method.
In the following example the PointOriginal
class defines an equality method but
-no inequality method. If this class is tested for inequality then a type error will be raised. The
-PointUpdated
class is better as it defines both an equality and an inequality method. To
-comply fully with the object model this class should also define a hash method (identified by
-a separate rule).
A class that implements an ordering operator
-(__lt__
, __gt__
, __le__
or __ge__
) should implement
-all four in order that ordering between two objects is consistent and obeys the usual mathematical rules.
-If the ordering is inconsistent with default equality, then __eq__
and __ne__
-should also be implemented.
-
Ensure that all four ordering comparisons are implemented as well as __eq__
and
-__ne__
if required.
It is not necessary to manually implement all four comparisons,
-the functools.total_ordering
class decorator can be used.
In this example only the __lt__
operator has been implemented which could lead to
-inconsistent behavior. __gt__
, __le__
, __ge__
, and in this case,
-__eq__
and __ne__
should be implemented.