With new-style
Python classes, one can help cut down on the memory cost associated with creating new instances. This is down by using the
__slots__ class attribute. When the
__slots__ attribute is used in the declaration of new-style classes, the
__dict__ attribute is not created with instances of the class. The creation of the
__dict__ attribute can be expensive in terms of memory when creating large numbers of instances. So, the questions that begs itself is why not use the
__slots__ attribute for all classes throughout an application? The simple answer is, because of the lack of flexibility offered by attributes that live inside memory that has been pre-allocated by a slot. The other problem with using the
__slots__ attribute for every class is the burden involved with maintaining all the slots, in all the classes. These could be in the several hundred range or more. So, this simply isn't feasible.
What is feasible, however, is to define
__slots__ attributes for smaller classes with few attributes. Another factor to consider is instantiation density of these classes. That is, the
__slots__ attribute is more beneficial with large numbers of instances because of the net memory savings involved. Consider the following example.
#Example; Using __slots__
import timeit
#A simple person class that defines slots.
class SlottedPerson(object):
__slots__=("first_name", "last_name")
def __init__(self, first_name="", last_name=""):
self.first_name=first_name
self.last_name=last_name
#A simple person class without slots.
class Person(object):
def __init__(self, first_name="", last_name=""):
self.first_name=first_name
self.last_name=last_name
#Simple test for the slotted instances.
def time_slotted():
person_obj=SlottedPerson(first_name="First Name", last_name="Last Name")
first_name=person_obj.first_name
last_name=person_obj.last_name
#Simple test for the non-slotted instances.
def time_non_slotted():
person_obj=Person(first_name="First Name", last_name="Last Name")
first_name=person_obj.first_name
last_name=person_obj.last_name
#Main
if __name__=="__main__":
#Initialize the timers.
slotted_timer=timeit.Timer("time_slotted()",\
"from __main__ import time_slotted")
non_slotted_timer=timeit.Timer("time_non_slotted()",\
"from __main__ import time_non_slotted")
#Display the results.
print "SLOTTED ",slotted_timer.timeit()
print "NON-SLOTTED",non_slotted_timer.timeit()
In this example, we have two very simple classes. The
SlottedPerson and the
Person classes are identical except for the fact that the
SlottedPerson class will always outperform
Person. This is because there are always going to be performance gains when the interpreter doesn't need to allocate memory.