Since local variables are simply accessed using the LOAD_FAST step from one bytecode, on the other hand, self.x will require you to first find self using LOAD_FAST and then access x on it, which is also difficult, since Python must first check whether it is a data descriptor or just a simple attribute of an instance, and based on this, its value is selected.
Typically, when working with methods in CPython, it is recommended to cache such highly repeated calls, because otherwise a new related object is created each time. I barely saw a case where a regular attribute was cached in order to get some performance benefits. Other implementations, such as PyPy and Pyston, have their own way of speeding up attribute searches. From the data model page:
Note that the conversion from a function object to an (unrelated or related) method of the object occurs every time an attribute is retrieved from a class or instance. In some cases, a fruitful optimization is to assign an attribute to a local variable and call this local variable.
One example of this might be list.append (see also: https://hg.python.org/cpython/file/f7fd2776e80d/Lib/heapq.py#l372_ ), for example, if you list.append list with a large number elements and for some reason cannot use list comprehension, then caching list.append provides slight acceleration:
>>> %%timeit lst = [] for _ in xrange(10**6): lst.append(_) ... 10 loops, best of 3: 47 ms per loop >>> %%timeit lst = [];append=lst.append for _ in xrange(10**6): append(_) ... 10 loops, best of 3: 31.3 ms per loop
Python 3.7
In Python 3.7, two new byte codes will appear to speed up loading and calling the method.
Two new LOAD_METHOD CALL_METHOD : LOAD_METHOD and CALL_METHOD to avoid creating instances of related method objects for method calls, which leads to an acceleration of method calls up to 20%. (Courtesy of Yuri Selivanov and INADA Naoki at bpo-26110 .)