While trying to tackle a more complex problem, I came to compare access speed to local variable vs member variables.
Here a test program:
#!/usr/bin/env python
MAX=40000000
class StressTestMember(object):
    def __init__(self):
        self.m = 0
    def do_work(self):
        self.m += 1
        self.m *= 2
class StressTestLocal(object):
    def __init__(self):
        pass
    def do_work(self):
        m = 0
        m += 1
        m *= 2
# LOCAL access test
for i in range(MAX):
    StressTestLocal().do_work()
# MEMBER access test
for i in range(MAX):
    StressTestMember().do_work()
I know it might look like a bad idea to instantiate StressTestMember and StressTestLocal on each iterations but it makes sense in the modeled program where these are basically Active Records.
After a simple benchmark,
The local version is ~33% faster while still part of a class. Why?
Locals should be faster When a line of code asks for the value of a variable x, Python will search for that variable in all the available namespaces, in order: local namespace - specific to the current function or class method.
Local (temporary) variables and method-argument variables are the fastest variables to access and update.
self.m += 1 means you have to look up a local variable called self and then find the attribute called m
Of course if you just have to look up a local variable, it will be faster without the extra step.
It can be useful to look at what is happening under the hood:
>>> import dis
>>> dis.dis(StressTestLocal.do_work)
 18           0 LOAD_CONST               1 (0)
              3 STORE_FAST               1 (m)
 19           6 LOAD_FAST                1 (m)
              9 LOAD_CONST               2 (1)
             12 INPLACE_ADD         
             13 STORE_FAST               1 (m)
 20          16 LOAD_FAST                1 (m)
             19 LOAD_CONST               3 (2)
             22 INPLACE_MULTIPLY    
             23 STORE_FAST               1 (m)
             26 LOAD_CONST               0 (None)
             29 RETURN_VALUE        
>>> dis.dis(StressTestMember.do_work)
 10           0 LOAD_FAST                0 (self)
              3 DUP_TOP             
              4 LOAD_ATTR                0 (m)
              7 LOAD_CONST               1 (1)
             10 INPLACE_ADD         
             11 ROT_TWO             
             12 STORE_ATTR               0 (m)
 11          15 LOAD_FAST                0 (self)
             18 DUP_TOP             
             19 LOAD_ATTR                0 (m)
             22 LOAD_CONST               2 (2)
             25 INPLACE_MULTIPLY    
             26 ROT_TWO             
             27 STORE_ATTR               0 (m)
             30 LOAD_CONST               0 (None)
             33 RETURN_VALUE        
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With