Managing Class Attributes In Python
Tags: Python Programming, Programming, Decorators, Property, Attributes

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.

This is what happens when you do that , lol.

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.

and 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.

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.

, 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.

>>> obj.attribute

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.

>>> del obj.attribute

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 :

>>> name = property()

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:

don’t mind the way i calculated my tax …. lol.

Here is the output.

>>> 2982.0

>>> 994000.0

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.