Python __slots__ Example

Usage of Python __slots__ special attribute.

1 Prevent dynamic instance attributes

Given a simple Python class below.

class User(object):
    pass

Then try to give an attribute to an User object:

>>> tom = User()
>>> print(tom.__dict__)
{}

>>> tom.name = 'Tom'
>>> print(tom.__dict__)
{'name': 'Tom'}

>>> print(tom.name)
Tom

>>> tom.__dict__['age'] = 20
>>> print(tom.age)
20
>>> print(tom.__dict__)
{'name': 'Tom', 'age': 20}

So, you can bind a new attribute to a Python object dynamically in runtime, by either using the dot syntax or adding the new attribute to __dict__ dictionary directly.

How to limit such behavior of Python? For example, we hope that the User object should only have following attributes: id, name, age. And no extra dynamic attributes are allowed.

This goal can be achieved by using the special attribute: __slots__.

class Member(object):
    __slots__ = ['id', 'name', 'age']

__slots__ above defines a collection of available attributes.

>>> jerry = Member()
>>> jerry.id = 1
>>> jerry.name = 'Jerry'
>>> jerry.age = 18
>>> 
>>> jerry.job = 'Student'
AttributeError: 'Member' object has no attribute 'job'
>>> jerry.address = 'New York'
AttributeError: 'Member' object has no attribute 'address'
>>> 
>>> print(jerry.__dict__)
AttributeError: 'Member' object has no attribute '__dict__'

2 Avoid the creation of a __dict__

Note that with a __slots__ definition the object will have no attribute __dict__. That is to say, if the class uses __slots__ to define attributes, Python will not allocate the __dict__ dictionary for its objects.

Without a __dict__ variable, your Python application may get potential considerable space savings in memory.

class Member(object):
    __slots__ = ['id', 'name', 'age']
    
class Student(object):
    def __init__(self, id, name, age):
        self.id = id
        self.name = name
        self.age = age

>>> import sys
>>> s = Student(1, 'Jack', 19)
>>> print(sys.getsizeof(s.__dict__))
96

>>> m = Member()
>>> m.id = 1
>>> m.name = 'jack'
>>> m.age = 19
>>> print(sys.getsizeof(m.__dict__))
AttributeError: 'Member' object has no attribute '__dict__'

Usage of __slots__ only prevents creation of a __dict__ on object. The Python class still has its __dict__ attribute.

class Member(object):
    __slots__ = ['id', 'name', 'age']

>>> print(Member.__dict__)
{'__slots__': ['id', 'name', 'age'], '__module__': '__main__', 'id': <member 'id' of 'Member' objects>, 'age': <member 'age' of 'Member' objects>, '__doc__': None, 'name': <member 'name' of 'Member' objects>}

3 __slots__ together with __dict__

Can instance's __dict__ attribute exist together with the __slots__?

The solution is simple: add the __dict__ to the __slots__ list.

class Member(object):
    __slots__ = ['id', 'name', 'age', '__dict__']

>>> m2 = Member()
>>> m2.id = 2
>>> m2.name = 'M2'
>>> m2.age = 11

>>> m2.job = 'Student'
>>> print(m2.__dict__)
{'job': 'Student'}

Comments