"""
Comparable object utilities.
This module provides a lightweight base interface for implementing rich
comparison operations on custom objects. Subclasses only need to implement
the :meth:`IComparable._cmpkey` method, and all comparison operators
(``==``, ``!=``, ``<``, ``<=``, ``>``, ``>=``) will be derived from it.
The module contains the following main components:
* :class:`IComparable` - Base interface for defining comparable objects
Example::
>>> from hbutils.model.compare import IComparable
>>> class MyValue(IComparable):
... def __init__(self, v: int) -> None:
... self._v = v
...
... def _cmpkey(self):
... return self._v
...
>>> MyValue(1) < MyValue(2)
True
"""
import operator as ops
from typing import Any, Callable
__all__ = [
'IComparable',
]
[docs]
class IComparable:
"""
Interface for a comparable object.
This class provides a base interface for creating comparable objects. Subclasses
only need to implement the :meth:`_cmpkey` method to enable all comparison
operations. The comparison is based on the key values returned by
:meth:`_cmpkey`.
Examples::
>>> from hbutils.model import IComparable
>>> class MyValue(IComparable):
... def __init__(self, v) -> None:
... self._v = v
...
... def _cmpkey(self):
... return self._v
...
>>> MyValue(1) == MyValue(1)
True
>>> MyValue(1) == MyValue(2)
False
>>> MyValue(1) != MyValue(2)
True
>>> MyValue(1) > MyValue(2)
False
>>> MyValue(1) >= MyValue(2)
False
>>> MyValue(1) < MyValue(2)
True
>>> MyValue(1) <= MyValue(2)
True
"""
def _cmpkey(self) -> Any:
"""
Function for getting a key value which is used for comparison.
:return: A value used to compare.
:rtype: Any
:raises NotImplementedError: This method must be implemented by subclasses.
.. note::
Subclasses must override this method to return a comparable value that
represents the object for comparison purposes.
"""
raise NotImplementedError # pragma: no cover
def _cmpcheck(self, op: Callable[[Any, Any], bool], other: Any, default: bool = False) -> bool:
"""
Internal method to perform comparison check between two objects.
:param op: The comparison operator function to apply.
:type op: callable
:param other: The other object to compare with.
:type other: object
:param default: The default value to return if types don't match.
:type default: bool
:return: Result of the comparison operation.
:rtype: bool
.. note::
This method checks if both objects are of the same type before performing
the comparison. If types differ, it returns the default value.
"""
if type(self) == type(other):
return op(self._cmpkey(), other._cmpkey())
else:
return default
[docs]
def __eq__(self, other: Any) -> bool:
"""
Check equality between two objects.
:param other: The other object to compare with.
:type other: object
:return: True if objects are equal, False otherwise.
:rtype: bool
.. note::
Returns True immediately if comparing with self (identity check).
Otherwise, compares using :meth:`_cmpkey` values if types match.
"""
if self is other:
return True
else:
return self._cmpcheck(ops.__eq__, other, default=False)
[docs]
def __ne__(self, other: Any) -> bool:
"""
Check inequality between two objects.
:param other: The other object to compare with.
:type other: object
:return: True if objects are not equal, False otherwise.
:rtype: bool
.. note::
Returns False immediately if comparing with self (identity check).
Otherwise, compares using :meth:`_cmpkey` values if types match.
"""
if self is other:
return False
else:
return self._cmpcheck(ops.__ne__, other, default=True)
[docs]
def __lt__(self, other: Any) -> bool:
"""
Check if this object is less than another object.
:param other: The other object to compare with.
:type other: object
:return: True if this object is less than other, False otherwise.
:rtype: bool
"""
return self._cmpcheck(ops.__lt__, other)
[docs]
def __le__(self, other: Any) -> bool:
"""
Check if this object is less than or equal to another object.
:param other: The other object to compare with.
:type other: object
:return: True if this object is less than or equal to other, False otherwise.
:rtype: bool
"""
return self._cmpcheck(ops.__le__, other)
[docs]
def __gt__(self, other: Any) -> bool:
"""
Check if this object is greater than another object.
:param other: The other object to compare with.
:type other: object
:return: True if this object is greater than other, False otherwise.
:rtype: bool
"""
return self._cmpcheck(ops.__gt__, other)
[docs]
def __ge__(self, other: Any) -> bool:
"""
Check if this object is greater than or equal to another object.
:param other: The other object to compare with.
:type other: object
:return: True if this object is greater than or equal to other, False otherwise.
:rtype: bool
"""
return self._cmpcheck(ops.__ge__, other)