Managing Class Attributes In Python
Hello guys, in this blog post we are going to dig down into some python programming trick , or do i say features we can leverage on as python developers, we all know every thing in python is an object, class is an object , instance is an object , function is an object etc.
We all know that any time we do:
object.attribute or object.attribute = value`
We are trying to get the attribute or set the attribute of an object which is very common in all python programs especially when working with classes.
So what ? why should you care?
So why should you worry about attribute management , now let me break it down , here are some possible scenarios :
USE CASE #1
We know in python we can access any class attribute by referencing it with the obj.attribute.call , which means our attributes are open for various operations such as read , write , delete. Here is an example in the code snippet below:
When this code is executed , below is the result:
>>> John Doe you a confirmed user
>>> John Doe you're not yet confirmed
You can see that, we have altered the state of our object outside the context of the class , when we reference the confirmed attribute, and set it to false.
Most times we don’t want that we want to set permission on our attribute , such as read-only , or read and write attribute access e.t.c. In other to prevent the outside world of changing the state of our class.
USE CASE #2
Sometimes we want to hook into the attribute and perform some changes to it at the point when it’s been accessed by calls like this:
>>> obj.attribute
>>> obj.attribute = value
attribute management play a big role here as we will see in this post, there are more use cases, but we’ll leave this two so as not to make this Post bulky.
Features :
Here are the python features that help us manage attribute access in our code
- The __getattr__ and __setattr__ methods, for routing undefined attribute fetches and all attribute assignments to generic handler methods.
- The __getattribute__ method, for routing all attribute fetches to a generic handler method.
- The property built-in , for routing specific attribute access to get and set handler functions.
- The descriptor protocol , for routing specific attribute accesses to instances of classes with arbitrary get and set handler methods, and the basis for other tools such as properties and slots.
In this post , i will be going through the last two features in the list above,
i promise to create another post that explain the other two.
Prerequisites
To make use of this post a basic knowledge of ‘ OOP’ python is needed , also if you are trying out the examples with python 2.x, note that you should use the new-style classes.
Property Protocol
The property protocol allows us to route a specific attribute’s get, set, and delete operations to functions or methods we provide, enabling us to insert code to be run automatically on attribute access, intercept attribute deletions, and provide documentation for the attributes if desired.
A property is created by assigning the result of a built-in function to a class attribute:
>>> attribute = property(fget, fset, fdel, doc)
The property attribute take four optional positional arguments where the first argument is the callback to run when when the attribute is referenced, this callback return a value.
The second argument is the callback to run when the object is been assign a value, this callback return None.
>>> obj.attribute = value
The third argument is ‘ fdel’ is a callback when a ‘ del ’ operation is performed on an attribute, this callback return None.
While the last argument doc is doc-string.
All this argument are optional if one of them is not passed, and we try performing the operation python raises an ‘ AttributeError’ Exception stating that the operation is not allowed .
enough of the story let me show you how it works in code
This code snippet on your left , does not do any thing meaningful, but it shows how the property built-in can be used when the attribute is referenced or assign a value , here is the result when the code is executed.
>>> getting attribute
>>> John Doe
>>> setting value
>>> This is person name
With the fact that we can hook into when an attribute is referenced, it a platform for us to create some attribute access modification here, where by we can only provide a read-only access to the name attribute used in this context.
Let us remove the ‘setName’ and ‘delName’ method that we passed into our property built-in it and give it a read-only access.
This is what we get when we run this code:
>>> getting attribute John Doe
Traceback (most recent call last):
File
"/home/user/path/python\_files/read\_only\_property.py", line 14,
in \<module\> person1.name = 'smith'
AttributeError: can't set attribute
With the above code, we’ve set the name attribute to be a read-only attribute, where by if we try to set a new value to it , we get an error.
What if we don’t want the attribute to be readable and writable? it’s simple, we set the attribute to the property built-in without passing it any argument like this :
And if the name attribute is fetched or set an Exception is raised.
>>> Traceback (most recent call last):
File
"/home/user/path/python\_files/pratice.py", line 13,
in \<module\> print(person1.name)
AttributeError: unreadable attribute
Let’s go to a more meaningful use case of property protocol, let say we have a class Employee that collects the name of the employee, and the salary at the point of instantiation , and we want it to deduct tax from the salary whenever, the salary attribute is referenced , the below code shows how:
Here is the output.
You can see that at the point of getting the attribute we have hooked into the process, and performed some code while returning the value.
Coding property with decorator
Coding property with this syntax above is fine but there is a more pythonic way of doing it with python decorators, this is possible because decorators are just simple function name rebind.
class Person:
@property
def name(self): ...
same as :
name = property(name)
Example:
Using the above employee class example,
i have now re-factored our code to use the property decorator syntax to perform the same effect as the other one. The property decorator has attribute for getting , setting and deleting attribute
@property.getter for specifying the get function
@attribute_name.setter for specifying the set function
@attribute_name.deleter for specifying the del function
the getter attribute on the property decorator function is optional
because a call to @property() take getter has it default
Conclusion
There you have it, one of the features for managing attribute access in python , there are more use cases of this features , still figuring them out as i learn more .
Note: I was suppose to also explain the descriptor protocol in this post, but i skipped that in order not to make this post bulky , a dedicated post on Descriptor protocol would be put up soon.
If you have suggestion , question , or you noticed an error in this post kindly comment below or reach out to me on twitter
Thanks , Happy Pythoning!