ezvine
ezvine

Reputation: 868

Python | Is accessing a variable from parent class slower than accessing the same variable from child class, using a child class object?

class Animal():
    def add(self):
        self.weight = 10
        self.color = 'Black'

class Bird(Animal):
    def add(self):
        self.feather_type = 'Long'
        super().add()


b = Bird()
b.add()
print(b.weight)
print(b.color)
print(b.feather_type) // will this be faster than above 2 statements ?

Is accessing of variables from parent class, from objects of child class, slower than accessing variable directly from child class ?

If there are lot of variables 10+ (including arrays and Data Model objects) in parent class, and each of them is used in child class, is it suggested to add those in each of child class and remove from parent class for better performance? (Sounds stupid when I'm writing this, as this is contradicting the whole inheritance concept)

Is it better to store them as local variable in child class functions, and then access them ? (In case it is used multiple times)

As in code, the variables are not initialized in __init__ method. Will this make program slower, in this scenario ? This is done in this way because, all the attributes of class is not necessary for all operations operation. So when and as required they are initialized and used. (Note : It is taken care that the required attributes are created before performing the operation).

If there are lot of variables 10+ (including arrays and Data Model objects) in parent class, and each of them is used in child class, is it suggested to add those in each of child class and remove from parent class for better performance? (Sounds stupid when I'm writing this, as this is contradicting the whole inheritance concept)

Upvotes: 0

Views: 1021

Answers (3)

bruno desthuilliers
bruno desthuilliers

Reputation: 77952

Instance attributes are stored in the instance, so inheritance is totally irrelevant here. Remember that when a parent class method is invoked on a child instance, what the method get as first argument (self) is the child instance:

>>> class Foo:
...    def __init__(self):
...        print("In Foo.__init__, self is {}".format(self))
... 
>>> class Bar(Foo): pass
... 
>>> b = Bar()
In Foo.__init__, self is <__main__.Bar object at 0x7fd230244f60>
>>> 

Is it better to store them as local variable in child class functions, and then access them ? (In case it is used multiple times)

Only for attributes that are used in a tight loop (attribute resolution is a bit slower than local variable resolution) AND profiling shows there's a bottleneck at this point. But don't expect major speedup either...

Practically speaking, if you have to worry about such micro-optimizations chances are the real solution is either to rethink your design and implementation or re-implement the critical sections of the code in C.

As in code, the variables are not initialized in init method. Will this make program slower

Not really, except for the overhead of calling obj.add(). BUT:

This is done in this way because, all the attributes of class is not necessary for all operations. So when and as required they are initialized and used.

Wrong design. If you want defered initialization for some attribute, use a property or some custom descriptor. This will make sure your objects will always have the expected attributes whatever happens.

If there are lot of variables 10+ (including arrays and Data Model objects) in parent class

"Lots of" attributes in a class is often a design smell. There's no hard and fast rule here - some cases do require quite a few attributes - but chances are your class has too many responsabilites and would be better refactored into a set of smallest classes each with one single, well-defined responsability.

And no, copy-pasting the parent's class attributes initialization to the child class will not speed up your code in anyway - it will just make maintenance uselessly harder and increase the risks of introducing bugs when changing either the parent or child class. I'd personnally label this as a complete WTF, but I'm not known for my innate sense of diplomacy ;-)

EDIT:

Actually one thing I have not mentioned here, another main reason why I'm creating the variables outside init, is because I'm using factory design pattern in my code. And I'm dynamically creating classes using

def _create(pkg): 
    exec('import api.' + pkg + 'Helper as Creator'); 
    return eval('Creator' + '.' + pkg + 'Helper()' )

Err... You might be interested in learning about some of Python's features like importlib.import_module and getattr. As a general rule, whenever you consider using eval or exec, be sure there's a better (=> safer, more explicit, and much more maintainable) solution. I've been using Python for 20+ years now (for personnal and professional projects) and I still have to find a case where I had a legitimate reason to use eval or exec.

Also, you didn't post the part of the code which actually "dynamically creates the class" but dynamically creating classes doesn't impose any limitation or workaround (vs "static" definitions) - you can still give your class a proper initializer, properties or any other custom descriptor etc. If you're also using exec̀̀ / eval to build your class, then here again there are much better ways.

My 2 cents...

Upvotes: 1

deceze
deceze

Reputation: 522636

The instance attributes will all be created on the instance object itself. Parent/child relationships are irrelevant there.

b.add()

This calls:

def add(self):
    ...

self here will be b. This now calls super.add(), which is again:

def add(self):
    ...

self here will still be b. The attributes are all added directly to the same object.

Arguably resolving the super().add() call will be a slight overhead, but it's absolutely negligible. So would any difference in accessing the attributes be, if it existed.

Upvotes: 1

Erotemic
Erotemic

Reputation: 5248

Lets benchmark it and find out. For testing things like these I like to use a module that I wrote called: timerit.

import timerit


class Animal():
    def add(self):
        self.weight = 10
        self.color = 'Black'


class Bird(Animal):
    def add(self):
        self.feather_type = 'Long'
        super().add()


b = Bird()
b.add()

ti = timerit.Timerit(1000000, bestof=100, verbose=2)
for timer in ti.reset('Access weight'):
    with timer:
        b.weight

for timer in ti.reset('Access color'):
    with timer:
        b.color

for timer in ti.reset('Access feather_type'):
    with timer:
        b.feather_type

This results in

Timed Access weight for: 1000000 loops, best of 100
    time per loop: best=347.005 ns, mean=370.222 ± 17.2 ns
Timed Access color for: 1000000 loops, best of 100
    time per loop: best=350.992 ns, mean=367.194 ± 9.5 ns
Timed Access feather_type for: 1000000 loops, best of 100
    time per loop: best=348.984 ns, mean=367.680 ± 11.9 ns

So, no it doesn't seem like there is any significant difference between these.

Upvotes: 2

Related Questions