This content originally appeared on Level Up Coding - Medium and was authored by Nikita Sarda
Redefining your Python Code
In the world of object-oriented programming, we’ve grown accustomed to defining classes with predetermined attributes and methods. However in Python, there is no default functionality to allocate a static amount of memory while creating the object to store all its attributes. Unlike traditional OOPs classes where attributes are generally pre-defined, Python allows adding, modifying, or removing attributes at any point during the object’s lifecycle. This means we can create objects that dynamically adapt to our needs, without the necessity of defining a rigid structure beforehand.
What are Dynamic Attributes?
Dynamic attributes in Python are terminologies for attributes that are defined at runtime, after creating the objects or instances. When we create an object from a class, the attributes of the object will be stored in a dictionary called __dict__which is used to get and set attributes. It allows us to dynamically create new attributes after the creation of the object. In Python we call all functions, methods also as an object. So you can define a dynamic instance attribute for nearly anything in Python.
This flexibility makes these objects ideal for situations where the data structure may not be known in advance or may change over time, such as in dynamic data processing, application configuration, or representation of constantly evolving entities.
Dynamic Attribute Assignment
Python allows adding new attributes to class objects at any time by simply assigning them a value or by using the built-in function setattr()as well.
Here in this example, there are two instances of class DynamicAttributeCheck (obj1, obj2). While both have same static attributes like id, name, obj1 has a dynamic attributes as well defined for itself only.
class DynamicAttributeCheck:
def __init__(self, id) -> None:
self.id = id
self.name = "abc"+self.id
# Driver code
obj1 = DynamicAttributeCheck("123")
obj2 = DynamicAttributeCheck("456")
print("obj1 object before:", vars(obj1))
print("obj2 object before:", vars(obj2))
# Adding Dynamic attribute to a class object
obj1.surname = "surname"
# Adding Dynamic attribute to a class object using setattr() method
setattr(obj1, "age", 25)
print("obj1 object after adding dynamic attribute: ",vars(obj1))
print("obj2 object after adding dynamic attribute to obj1 only: ",vars(obj2))
Output:
obj1 object before: {'id': '123', 'name': 'abc123'}
obj2 object before: {'id': '456', 'name': 'abc456'}
obj1 object after adding dynamic attribute: {'id': '123', 'name': 'abc123', 'surname': 'surname', 'age': 23}
obj2 object after adding dynamic attribute to obj1 only: {'id': '456', 'name': 'abc456'}
Limitations of Dynamic Attributes
While dynamic attributes offer great flexibility and dynamic manipulation of objects at runtime, they add on various limitations as well:
1. Performance: As every object in Python contains a dynamic dictionary that allows adding attributes. For every instance object, we will have an instance of a dictionary that consumes more space and wastes a lot of RAM making code slower.
2. Code Clarity: The lack of static typing can lead to errors if not handled carefully. It makes it difficult to debug code when objects keep on accumulating new attributes. It can also make the code less evident if this flexibility is abused.
How to restrict this dynamic behaviour?
Limiting the creation of attributes at runtime can be a great advantage. One way to stop this dynamic attribute assignment is by using '__slots__'. __slots__ allow us to explicitly declare data members and deny the creation of __dict__ and __weakref__ . Hence it allows us to fix the instance-level attributes a class object can ever possess.
Example of Slotted Class:
Here one simply needs to define a __slots__ attribute list at class level with all attributes its object may possess.
class DynamicAttributeCheck:
__slots__ = ["id", "name"]
def __init__(self, id) -> None:
self.id = id
self.name = "abc"+self.id
Now, if we try to add a new attribute at run-time as we saw in previous example, it will raise an AttributeError:
>>> obj1 = DynamicAttributeCheck("123")
>>> obj1.age = 25
AttributeError: 'DynamicAttributeCheck' object has no attribute 'age'
In fact, there is no more '__dict__' attribute present for instances of slotted classes.
>>> obj1.__dict__
AttributeError: 'DynamicAttributeCheck' object has no attribute '__dict__'
However, when inheriting from a class without __slots__, the __dict__ attribute of the instances will always be accessible. Also, one can always add '__dict__’ to the sequence of strings in the __slots__ declaration if dynamic assignment of new variables is desired.
How __slots__ work?
When a class defines __slots__, it replaces instance dictionaries with a fixed-length array of slot values. Internally, __slots__ automatically creates a descriptor for each attribute with the implementation of descriptor methods like __get__(), __set__() and __delete__(). It means that the object will use these descriptor methods to interact with attributes instead of the default dictionary behaviour. The implementation of __get__(), __set__() uses an array instead of the dictionary and it’s entirely implemented in C which is highly efficient.
Advantages of using __slots__
- The space saved over using __dict__ can be significant. An object of the slotted class consumes aprrox. 2.5 times lesser memory than an object of a normal class.
- Attribute lookup speed can be significantly improved as well. __slots__ has slightly better performance because the time complexity of get/set operation in a list is faster than a dictionary in the worst case.
When to avoid __slots__?
- Multiple Inheritance: You need to be clear about what you are doing and what you want to achieve with __slots__, especially when inheriting a class with it. You should only declare a particular slot one time in an inheritance tree. Multiple “parent classes with nonempty slots” cannot be combined. The order of inheritance, the attribute names can make a huge difference in the performance.
- You can’t inherit a built-in type such as int, bytes, tuple with non-empty __slots__. Besides, you can’t assign a default value to attributes in __slots__. This is because these attributes are supposed to be descriptors. Instead, you can assign the default value in __init__().
As a final note, while dynamic attributes have their own flexibility and uses, they have bigger downside as well. So if you don’t want to dynamically add new attributes to an object, it is better to create a slotted class. In fact, even if you know the attributes that a class object will ever possess, but some of these attributes are not available during the object’s initialisation, you can still declare them in the __slots__ class attribute and assign a value to them later in the program whenever they are available.
Few reference links to read more about __slots__
Dynamic Attributes and __slots__ in Python was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.
This content originally appeared on Level Up Coding - Medium and was authored by Nikita Sarda

Nikita Sarda | Sciencx (2024-07-26T16:01:11+00:00) Dynamic Attributes and __slots__ in Python. Retrieved from https://www.scien.cx/2024/07/26/dynamic-attributes-and-__slots__-in-python/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.