views:

139

answers:

1

I have a question about how python treats the methods passed to sorted(). Consider the following small script:

#!/usr/bin/env python3

import random

class SortClass:
    def __init__(self):
        self.x = random.choice(range(10))
        self.y = random.choice(range(10))
    def getX(self):
        return self.x
    def getY(self):
        return self.y

if __name__ == "__main__":
    sortList = [SortClass() for i in range(10)]
    sortedList = sorted(sortList, key = SortClass.getX)
    for object in sortedList:
        print("X:", object.getX(),"Y:",object.getY())

Which gives output similar to the following:

X: 1 Y: 5
X: 1 Y: 6
X: 1 Y: 5
X: 2 Y: 8
X: 2 Y: 1
X: 3 Y: 6
X: 4 Y: 2
X: 5 Y: 4
X: 6 Y: 2
X: 8 Y: 0

This script sorts the SortClass objects according to the value of each instance's x field. Notice, however, that the "key" argument of sorted points to SortClass.getX, not to any particular instance of SortClass. I'm a bit confused as to how python actually uses the method passed as "key". Does calling getX() like this work because the objects passed to it are of the same type as the "self" argument? Is this a safe use of the "key" argument?

+4  A: 

Methods on classes are just functions.

class MyClass(object):
...     def my_method(self): pass
...
>>> MyClass.my_method
<function my_method at 0x661c38>

When you fetch a method from an instance of a class, Python uses some magic (called descriptors) to return a bound method. Bound methods automatically insert the instance as the first argument when they are called.

>>> MyClass().my_method
<bound method MyClass.my_method of <__main__.myClass object at 0x6e2498>>

However, as you have noticed, you can also directory call the function with an instance as the first argument: MyClass.my_method(MyClass())

That's what happening with sorted(). Python calls the function SortedClass.getx with the item in the list which happens to be an instance of SortedClass.

(By the way, there are better ways to do what you are doing. First of all, don't use methods to expose attributes like getX. Use properties or plain attributes. Also, look at operator.itemgetter and operator.methodcaller.)

Benjamin Peterson