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:
__slots__ = ['var1']
def __init__(self, value_passed_through_here):
self.var1 = value_passed_through_herea1 = A1(1); a1.var11
a has been created and everything is going well. Let’s try adding another attribute.
a1.var2 = "but can I set another var?"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.
a1.__slots__ = ['var2']AttributeError: 'A1' object attribute '__slots__' is read-only
a1.__slots__ = ['var1', 'var2']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_herea2a = A2A(1)var1 shows up as expected when creating an object
a2a.__dict__{'var1': 1}
a2a.var2 = 'adding a second thing'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:
__slots__ = ['var1', '__dict__']
def __init__(self, value_passed_through_here):
self.var1 = value_passed_through_herea2b = A2B(1)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.var11
a2b.var2 = "test if we can add new variables now"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:
__slots__ = ['a']
def __init__(self, a):
self.a = aa3 = A3(1); a3.a1
class B3A(A3):
def __init__(self):
self.a = 1
self.b = 2b3a = B3A()b3a.__slots__['a']
b3a.__dict__{'b': 2}
b3a.a1
b3a.c = "can I set 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):
__slots__ = ['a','b']
def __init__(self):
self.a = 1
self.b = 2b3b = B3B()b3b.c = "can I set this?"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.