سؤال

How can I check if an object is orderable/sortable in Python?

I'm trying to implement basic type checking for the __init__ method of my binary tree class, and I want to be able to check if the value of the node is orderable, and throw an error if it isn't. It's similar to checking for hashability in the implementation of a hashtable.

I'm trying to accomplish something similar to Haskell's (Ord a) => etc. qualifiers. Is there a similar check in Python?

هل كانت مفيدة؟

المحلول

If you want to know if an object is sortable, you must check if it implements the necessary methods of comparison.

In Python 2.X there were two different ways to implement those methods:

  1. cmp method (equivalent of compareTo in Java per example)

    __cmp__(self, other): returns >0, 0 or <0 wether self is more, equal or less than other

  2. rich comparison methods

    __lt__, __gt__, __eq__, __le__, __ge__, __ne__

    The sort() functions call this method to make the necessary comparisons between instances (actually sort only needs the __lt__ or __gt__ methods but it's recommended to implement all of them)

In Python 3.X the __cmp__ was removed in favor of the rich comparison methods as having more than one way to do the same thing is really against Python's "laws".

So, you basically need a function that check if these methods are implemented by a class:

# Python 2.X
def is_sortable(obj):
    return hasattr(obj, "__cmp__") or \
           hasattr(obj, "__lt__") or \
           hasattr(obj, "__gt__")

# Python 3.X
def is_sortable(obj):
    cls = obj.__class__
    return cls.__lt__ != object.__lt__ or \
           cls.__gt__ != object.__gt__

Different functions are needed for Python 2 and 3 because a lot of other things also change about unbound methods, method-wrappers and other internal things in Python 3.

Read this links you want better understanding of the sortable objects in Python:

PS: this was a complete re-edit of my first answer, but it was needed as I investigated the problem better and had a cleaner idea about it :)

نصائح أخرى

Regrettably it is not enough to check that your object implements lt. numpy uses the '<' operator to return an array of Booleans, which has no truth value. SQL Alchemy uses it to return a query filter, which again no truth value. Ordinary sets uses it to check for a subset relationship, so that

set1 = {1,2}
set2 = {2,3}
set1 == set2
False
set1 < set2
False
set1 > set2
False

The best partial solution I could think of (starting from a single object of unknown type) is this, but with rich comparisons it seems to be officially impossible to determine orderability:

 if hasattr(x, '__lt__'):
     try:
         isOrderable = ( ((x == x) is True) and ((x > x) is False)
                         and not isinstance(x, (set, frozenset)) )
     except:
         isOrderable = False
 else:
     isOrderable = False

Edited

As far as I know, all lists are sortable, so if you want to know if a list is "sortable", the answer is yes, no mather what elements it has.

class C:
    def __init__(self):
        self.a = 5
        self.b = "asd"

c = C()    
d = True

list1 = ["abc", "aad", c, 1, "b", 2, d]
list1.sort()
print list1
>>> [<__main__.C instance at 0x0000000002B7DF08>, 1, True, 2, 'aad', 'abc', 'b']

You could determine what types you consider "sortable" and implement a method to verify if all elements in the list are "sortable", something like this:

def isSortable(list1):
    types = [int, float, str]
    res = True
    for e in list1:
        res = res and (type(e) in types)
    return res

print isSortable([1,2,3.0, "asd", [1,2,3]])

While the explanations in answers already here address runtime type inspection, here's how the static types are annotated by typeshed. They start by defining a collection of comparison Protocols, e.g.

class SupportsDunderLT(Protocol):
    def __lt__(self, __other: Any) -> bool: ...

which are then collected into rich comparison sum types, such as

SupportsRichComparison = Union[SupportsDunderLT, SupportsDunderGT]
SupportsRichComparisonT = TypeVar("SupportsRichComparisonT", bound=SupportsRichComparison)

then finally these are used to type e.g. the key functions of list.sort:

@overload
def sort(self: list[SupportsRichComparisonT], *, key: None = ..., reverse: bool = ...) -> None: ...
@overload
def sort(self, *, key: Callable[[_T], SupportsRichComparison], reverse: bool = ...) -> None: ...

and sorted:

@overload
def sorted(
    __iterable: Iterable[SupportsRichComparisonT], *, key: None = ..., reverse: bool = ...
) -> list[SupportsRichComparisonT]: ...
@overload
def sorted(__iterable: Iterable[_T], *, key: Callable[[_T], SupportsRichComparison], reverse: bool = ...) -> list[_T]: ...
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top