user9200028
user9200028

Reputation:

Expected behaviour of custom instance method sort

In Python 2.7, list objects implement a sort method taking the three arguments cmp, key, and reverse, as described here (see note 8), the first two of which default to the value None, and the last defaulting to False.

Suppose one is interested in implementing a class providing an instance method named sort with the same signature, i.e., taking the same arguments, as list.sort. For list.sort, the cmp and key arguments may induce different orders on the list to be sorted and may therefore be incompatible.

My question is whether there is a best practice one should follow in implementing the instance method sort in the case where both cmp and key are different from None?

For clarity, consider the following example scenario:

class Table(object):

    """Stores data by rows."""

    def __init__(self, *rows):
        self._rows = list(rows)

    def sort(self, cmp=None, key=None, reverse=False):
        """Sort table rows."""

        if cmp and key:
            # What behaviour would an experienced Python programmer
            # expect in this situation? Raise a ValueError? Prefer
            # one of cmp and key? Or is there no best practice here?
            raise ValueError('I am partial to raising a ValueError.')

        # The implementation could be more involved than pushing the
        # call to sort forward to self._rows, for instance if data was
        # stored by columns.
        self._rows.sort(cmp, key, reverse)

Note: The question raised here is not an issue in Python 3.6, where the argument cmp is not supported by list.sort (source).

Upvotes: 0

Views: 42

Answers (1)

glibdud
glibdud

Reputation: 7860

I think the premise a little flawed here:

For list.sort, the cmp and key arguments may induce different orders on the list to be sorted and may therefore be incompatible.

cmp and key aren't generally incompatible because they do different things. key defines how to map the members of the list to a value which they will be sorted by, and cmp defines how to determine the relative order of any two of those values. It's perfectly valid (though rarely a good idea) to use both:

>>> mylist = [(2, 'two'), (3, 'three'), (7, 'seven')]
>>> mylist.sort(key=lambda a:a[1], cmp=lambda a,b:ord(a[-1])-ord(b[-1]))
>>> mylist
[(3, 'three'), (7, 'seven'), (2, 'two')]

There I've sorted the list by the last letter of the second item. I could have used cmp or key individually to do the job (and the advice these days is to use key), but if I really want to, using both works just fine.

All that said, your example of making the sort method just a wrapper for sorting an internal list seems pretty trivial. Why do any validation at all if you want to match the functionality of the built-in list.sort()? Just pass everything straight through:

def sort(self, *args, **kwargs):
    self._rows.sort(*args, **kwargs)

Upvotes: 2

Related Questions