Is there a performance impact when using a method instead of a function?
I like working with classes and methods instead of simple functions. I'm wondering if there is any specific performance impact (either in execution speed, or in memory usage, or other aspects).
A quick test shows that both perform equally well:
import timeit
class Hello:
def hello(self):
x = 9 * 8 + 3**5
def world():
x = 9 * 8 + 3 ** 5
print(timeit.timeit(world, number=10000000))
h = Hello()
print(timeit.timeit(h.hello, number=10000000))
# 0.8460009839758439
# 0.8781686117747095
In other tests, I have not seen more RAM used in one case than in another.
Are there specific cases where performance will degrade when using a class / method instead of a function?
Note. I would like to focus solely on the performance of the code, not the aesthetic aspects.
source to share
The method call overhead is simply object function
to object conversion method
when the attribute ( .
) accesses the function attribute it made from the instance.
In addition, the function call is similar, with one additional argument ( self
) entered implicitly for the method.
So, no, there is no problem here, the overhead is small and can be completely eliminated by assigning the method to a local variable:
meth = h.hello
# use meth from now on
(Edit: In Python where 3.7
new opcodes are introduced, which basically negates the advantage of assigning a h.hello
local name, finding methods just got pretty quick :-)
If you are looking for bottlenecks, you should look elsewhere. Python's dynamic interpretation really makes these kinds of problems pedantic.
As far as the memory aspect is concerned, the methods must be a bit more functions because of the method essentially containing a function as one of its members:
meth.__func__ # original function object
Regardless, I cannot imagine a scenario where your application is choking due to small ways to inject memory overhead.
In CPython, for example, about 64 bytes are added for the bound method object according to getsizeof
:
>>> getsizeof(Foo().foo)
64
It is not considered an attribute __func__
that contains objects function
:
>>> getsizeof(Foo().foo.__func__)
136
source to share
Method invocation obj.method(...)
involves accessing attributes (part of it obj.method
), which can be non-trivial and therefore quite costly. A brief description of a possible scenario for accessing attributes is contained in the documentation for the descriptor protocol :
The default behavior for accessing attributes is getting, setting, or removing an attribute from an object dictionary. For example,
a.x
has starting witha.__dict__['x']
, thentype(a).__dict__['x']
continuing with base classestype(a)
excluding metaclasses.However, if the value you are looking for is an object that defines one of the descriptor, then Python can override the default behavior and the descriptor method is called instead. Where this happens in the precedence chain depends on which descriptor methods were defined and how they were called.
Only after the access to the attribute is complete is the call to the resulting callable object slightly different from the call to the free function. Note, however, that in your test, the attribute access overhead, that is, operations behind a seemingly harmless expression h.hello
, is not measured (although it should be pretty small in your example).
source to share