Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is there a magic method for sorted() in Python?

I understand that there are magic methods in python that can be overwritten by classes to control the way certain built in functions treat the members of these classes. For example, the behavior of len() and str() can be overwritten via magic methods __len__() and __str__():

class EmptySet(object):
    def __len__(self):
        return 0

    def __str__(self):
        return '[]'

>>> e = EmptySet()
>>> str(e)
[]

>>> len(e)
0

There are also __cmp__() and __ge__(), __le__() etc methods to control how these objects can be compared and how a list of them should be ordered by list.sort(). My question is not about customizing the ordering of objects in a list but about sorting the object itself. Suppose the set weren't empty and I want to use sorted() to sort it:

class SetOfTwo(object):
    def __init__(self, a , b):
        el_0 = a
        el_1 = b

    def __len__(self):
        return 2

    def __str__(self):
        return '[{}, {}]'.format(el_0, el_1)

Is there a magic method I can implement to have sorted() flip the elements if they aren't in order? I'm picturing the following behavior:

>>> s = SetOfTwo(2, 1)
>>> str(s)
[2, 1]

>>> t = sorted(s)
>>> str(t)
[1, 2]

>>> type(t)
>>> SetOfTwo
like image 271
gregrf Avatar asked Feb 19 '18 14:02

gregrf


2 Answers

You should definitely read the official documentation of how to emulate container types. Basically a class supposed to work as a container (list, dict etc.) needs to implement methods to set or get members __getitem__(), __setitem__() and iterate over items __iter__() and to get the number of items - method __len__(). This is the minimum. But you can also add the ability to delete items and other operations.

The behaviour of sorted() built-in function is to iterate over elements of your container and compare them using methods you mentioned __cmp__(), __ge__(), __le__() which should be defined for items and not the container as you know already. Then a new list instance is created with items sorted and this new instance is returned. You can pass it to the constructor of your custom container then or you can wriap sorted() with a custom function which will return the desired class instance.

like image 150
ElmoVanKielmo Avatar answered Sep 24 '22 23:09

ElmoVanKielmo


As some have said in the comments, sets are unordered but I don't think your question is really about sets.

Python uses the data model methods you mentioned, ge, le, and cmp to determine how a class behaves when sorted() is called on it. You can see how I try to call it here, but Python objects and asks me to implement <.

>>> class a(object):
...   pass
...
>>> b = a()
>>> c = a()
>>> d = [b, c]
>>> sorted(d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'a' and 'a'

Hope this helps. Aslo, as other people said, it's a good idea to subclass something in collections.abc. I'd read Item 28 in effective python that talks about this to get a good idea.

like image 45
lieblos Avatar answered Sep 24 '22 23:09

lieblos