how to use __slots__
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
print(f'x: {p.x}')
print(f'y: {p.y}')
__slots__
is a special class variable in Python that restricts the attributes that can be assigned to an instance of a class.
__slots__ is
an iterable(usually a tuple) that stores the names of the allowed attributes for a class. It specifies the list of attribute names that can be assigned to instances of the class.
If __slots__
is defined, an exception will be raised if an attribute that is not in the list is assigned to an instance.
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
self.z = 5 #Raises an error because 'z' is not in __slots__
p = Point(2, 3)
In the above example, an AttributeError
exception is raised, because we tried to assign attribute z
to an instance when "z"
is not present in the __slots__
list.
We also cannot add attributes to an instance dynamically.
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(2, 3)
p.z = 5 #Raises an exception
Why is __slots__ important?
By default, class instances have a dictionary called __dict__
that stores all the attributes, and their associated values.
the __dict__ variable
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
print("__dict__: ", p.__dict__)
Using the dictionary, __dict__
, allows for dynamic creation of attributes at runtime, which can be convenient but can also lead to potential memory inefficiency.
The __slots__
variable allows for more efficient memory usage as it limits the attributes that can be assigned to an instance to only those listed in __slots__
declaration.
Using __slots__
can offer considerable performance benefits because it eliminates the need for a dictionary to store attributes. When __slots__
is declared, the attributes are stored in a fixed-size array on the object itself. This makes accessing and setting attributes much faster as the interpreter does not have to perform dictionary lookups.
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
print("__slots__: ", p.__slots__)
print("__dict__: ", p.__dict__) #The __dict__ is not used when __slots__ is declared.
__slots__
is particularly useful when we know exactly what attributes class instances should have, and we don't want to assign arbitrary attributes dynamically at runtime.
__slots__ with inheritance
When a class defines __slots__
, it only applies to instances of that class and does not automatically apply to its child classes. A child class does not inherit the parent class's __slots__
.
By default, a child class will still use the __dict__
dictionary for storing the attribute, in addition to parents __slots__
, unless it also defines its own __slots__
.
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
self.z = 10 #Raises an error because 'z' is not in __slots__
#inherits from Point
class Point3d(Point):
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z #does not raise an error
p = Point3d(2, 3, 4)
print('__slots: ', p.__slots__) #parents __slots__
print("__dict__: ", p.__dict__) #child's __dict__
print("x: ", p.x)
print("y: ", p.y)
print("z: ", p.z)
A child class can also declare its own __slots__
, without including those declared by its parent classes.
class Point:
__slots__ = ["x", "y"]
def __init__(self, x, y):
self.x = x
self.y = y
self.z = 10 #Raises an error because 'z' is not in __slots__
#inherits from Point
class Point3d(Point):
__slots__ = ("z", )
def __init__(self, x, y, z):
super().__init__(x, y)
self.z = z #does not raise an error
p = Point3d(2, 3, 4)
print("x: ", p.x)
print("y: ", p.y)
print("z: ", p.z)