This is a simple timing experiment to explore whether it is possible in Python to gain performance benefit by calling a method via a direct reference instead of accessing it as an object’s attribute.

Let’s suppose we have a loop that contains a call to an object’s method:

for _ in range(n):
    ...
    obj.method()
    ...

In theory, if n is a large number, it should make sense to refactor the loop like this:

obj_method = obj.method

for _ in range(n):
    ...
    obj_method()
    ...

i.e. to store a reference to the method and then call it directly using this reference as a shortcut. This technique should save us n attribute look-ups (method resolutions) which should result in reduced execution time.

Let’s verify this assumption. To minimize overhead, we will define and call an empty method. The time taken will be measured by the timeit.timeit function.

from timeit import timeit


class Class:
    def method(self):
        pass


obj = Class()

lookup_time = timeit('obj.method()', number=10_000_000, globals=vars())
print(f'Look-up: {lookup_time:.3f} s')

obj_method = obj.method    # store reference to method

shortcut_time = timeit('obj_method()', number=10_000_000, globals=vars())
print(f'Shortcut: {shortcut_time:.3f} s')

Output of three sample runs of the program:

Look-up: 1.148 s
Shortcut: 1.036 s
Look-up: 1.173 s
Shortcut: 1.107 s
Look-up: 1.150 s
Shortcut: 1.022 s

We can see that there is indeed a consistent difference (around 9%) in execution time.

One thing to note here is that instance methods in Python are not resolved by the means of simple attribute look-up. Rather, a descriptor protocol’s getter mechanism is used to retrieve bound methods. I assume that this adds another tiny amount of overhead that is incurred during method calls and that our shortcut saves us. Quoting the official documentation:

Note that the transformation from function object to instance method object happens each time the attribute is retrieved from the instance. In some cases, a fruitful optimization is to assign the attribute to a local variable and call that local variable.

Final note

This shortcut trick should probably be used sparsely, as I think its use cases are quite limited. It would perhaps make sense in some critical loop with many method calls, and only in cases when the loop represents an actual bottleneck in the program.