Python like many other dynamic programming languages can be extended. Objects and definitions can be modified at runtime. This gives the developer great power but this power comes at a price.
During the execution two instances of the same object can have
different properties. This is why python accesses these properties
through a dictionary __dict__
. This flexibility comes at the expense
of CPU and memory. Most of the time this is not a big deal, but it can
become one if you have tens of thousand of instances of an object with
many attributes and long attribute names.
Here is how it works.
class Collection(object):
def __init__(self):
self._collection_data = set([])
def add(self, elem):
self._collection_data.add(elem)
def __repr__(self):
return ', '.join(self._collection_data)
In [2]: c = Collection()
In [3]: c.add('this is my data')
In [4]: c
Out[4]: this is my data
In [5]: c.__dict__
Out[5]: {'_collection_data': set(['this is my data'])}
You can now create a new property. This new property will be added
into __dict__
In [6]: c.new_property = 'hello property'
In [7]: c.__dict__
Out[7]: {'_collection_data': set(['this is my data']),
new_property': 'hello property'}
In [8]: c.new_property
Out[8]: hello property
By setting the attribute __slots__
with the list of the properties
your object will handle, python will no longer use __dict__
to
dynamically manage the object properties. Your object instances will
no longer be dynamic. Meaning you won't be able to set other
properties other than the ones that have already be defined in
__slots__
.
You can use __slots__
when you know in advance what will be the
properties of your class and you know that no other property will be
added at runtime. __slots__
renders your class static in the same
way a struct in C or C++ would.
The __slots__
attribute tells python to not create a __dict__
for
every instance of the object. Creating a new property will raise an
AttributeError exception.
class Collection(object):
__slots__ = ['_collection_data']
def __init__(self):
self._collection_data = set([])
def add(self, elem):
self._collection_data.add(elem)
def __repr__(self):
return ', '.join(self._collection_data)
In [2]: c = Collection()
In [3]: c.add(10)
In [4]: c.new_property = 12
AttributeError: 'Collection' object has no attribute 'new_property'
In [6]: dir(c)
Out[6]:
['__class__',
'__delattr__',
'__doc__',
'__format__',
'__getattribute__',
'__hash__',
'__init__',
'__module__',
'__new__',
'__reduce__',
'__reduce_ex__',
'__repr__',
'__setattr__',
'__sizeof__',
'__slots__',
'__str__',
'__subclasshook__',
'_collection_data',
'add']
When you use __slots__
, the instance created for that class has no
__dict__
attribute. Instead, all properties access is done directly.
Slots change the behavior of a Python class and should only be considered if you are creating thousands of instances of an object.
They can be abused by control freaks and static typing lovers. Python is and should remain dynamic. Control freaks should consider abusing metaclasses and static typing lovers should play with decorators.