Python Version Check:
3.8.8 (default, Apr 13 2021, 19:58:26)
[GCC 7.3.0]
Python Native slots Attribute Exploration
Introduction
While reading the python data model documentation, I came across something I hadn’t seen before. __slots__
is an optional argument that allows users to “explicitly declare data members”. It is an interesting concept that I haven’t seen utilized, but perhaps the reason is that not many people are aware it exists. I am going to explore this attribute that is available to see if it might provide value for my future projects. According to this blog post, __slots__ can significantly reduce the amount of ram required to create objects (40-50%!). Now let’s dive in and figure out how it’s used!
Example #1: Typical Case
The first example we look at is the working example. we will have a class A1
with slots set to accept one var named var1
.
class A1:
= ['var1']
__slots__ def __init__(self, value_passed_through_here):
self.var1 = value_passed_through_here
= A1(1); a1.var1 a1
1
a
has been created and everything is going well. Let’s try adding another attribute.
= "but can I set another var?" a1.var2
AttributeError: 'A1' object has no attribute 'var2'
a.var2
fails as expected because it isn’t in the __slots__
list and __slots__
is read-only so it cannot be updated.
= ['var2'] a1.__slots__
AttributeError: 'A1' object attribute '__slots__' is read-only
= ['var1', 'var2'] a1.__slots__
AttributeError: 'A1' object attribute '__slots__' is read-only
When __slots__ is used, the __dict__ value is not set. Let’s explore that a little further though.
a1.__dict__
AttributeError: 'A1' object has no attribute '__dict__'
Example #2a: Exploring __dict__ Without Using __slots__
class A2A:
def __init__(self, value_passed_through_here):
self.var1 = value_passed_through_here
= A2A(1) a2a
var1 shows up as expected when creating an object
a2a.__dict__
{'var1': 1}
= 'adding a second thing' a2a.var2
Adding a second variable adds it to the __dict__ as expected
a2a.__dict__
{'var1': 1, 'var2': 'adding a second thing'}
Example #2b: Exploring __dict__ When Using __slots__
class A2B:
= ['var1', '__dict__']
__slots__ def __init__(self, value_passed_through_here):
self.var1 = value_passed_through_here
= A2B(1) a2b
a2b.__dict__
{}
__dict__ exists now since we added it to __slots__, but it isn’t populating the __dict__ like normal. We are still able to call the attribute var1
though.
a2b.var1
1
= "test if we can add new variables now" a2b.var2
Surprisingly, once we add __dict__ to the __slots__ list, adding a new var works.
a2b.__dict__
{'var2': 'test if we can add new variables now'}
When we look at __dict__ after adding var2, there is an entry in __dict__ as well.
a2b.var2
'test if we can add new variables now'
So if we enable __dict__ we are able to add new items to the __dict__, but __dict__ has to be explicitly defined to work.
Example 3: Inheritance
Now that we’ve explored __slots__, let’s see how it behaves when one class is inherited from another.
class A3:
= ['a']
__slots__ def __init__(self, a):
self.a = a
= A3(1); a3.a a3
1
class B3A(A3):
def __init__(self):
self.a = 1
self.b = 2
= B3A() b3a
b3a.__slots__
['a']
b3a.__dict__
{'b': 2}
b3a.a
1
= "can I set c?" b3a.c
b3a.__dict__
{'b': 2, 'c': 'can I set c?'}
So when B3A
is inherited from A3
, it uses the slots class, but it also reverts back in a lot of ways to a normal, non-slots, class again. The last thing I’m going to try is actually setting a __slots__
in B3B
just to see what happens
class B3B(A3):
= ['a','b']
__slots__ def __init__(self):
self.a = 1
self.b = 2
= B3B() b3b
= "can I set this?" b3b.c
AttributeError: 'B3B' object has no attribute 'c'
So now that we have given B3B
a __slots__
it is no longer behaves the same way that B3A
is.
Here is what the official documentation says about this: >The action of a __slots__ declaration is not limited to the class where it is defined. __slots__ declared in parents are available in child classes. However, child subclasses will get a __dict__ and __weakref__ unless they also define __slots__ (which should only contain names of any additional slots). (https://docs.python.org/3.8/reference/datamodel.html#notes-on-using-slots 5th bullet)
Conclusion
__slots__ is an interesting concept that is built into Python that I hadn’t heard of and wanted to explore. Hopefully this notebook is informative to other Python users as well. Things didn’t always behave as I would have expected and that’s part of the fun of actually testing out the code to see how things work in practice.