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 dicts or sets, 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.