Alternative ways to control attributes in an object
The following examples represent alternative ways to control attributes in Python. As a proviso, I do not attest to them being the best way to control attributes, rather alternatives means that can add to your arsenal of tools to use in Python.
The section to follow will introduce an example-led guide to using decorators to manage attributes.
Example 2: Using @staticmethod Decorator
For continuity, the same Employees class will be initialised in the proceeding examples. Here, the staticmethod decorator can be used to check the attribute value in an object. Specifically, here, we want both the age and service_length attribute values to be of type int.
To do this, we can define a data_type_check method in our class, and decorate it with the staticmethod decorator. When the attributes are being set in the init constructor, we call the method data_type_check on our object, and pass in either age or service_length as arguments. If age, and service length are of type int, they will be returned and set as attributes in our object. If they are not of type int, a type error exception will be raised.
One thing that is really nice about using the staticmethod decorator, is its re-usability. If, for example, we added an annual bonus argument to our init method, we could simply call the staticmethod decorator again and pass bonus as an argument.
Example 3: Using a Custom Decorator
Another way to control the value of attributes is via a custom decorator. In keeping with our theme, let’s create the same class, but this time decorate the init constructor method, with a function called attr_check.
When we create an instance of our Employee class, the init method will be called automatically. As the attr_check function decorates the init method, this function will be called. attr_check will take the init method as an argument, and return inner. When we then call inner with our object ref, age and service, we can perform type checking on both age and service. If age and service are of type int, we will return the original init method with the object, name, age and service and arguments.
This implementation is a nice way to control attribute values in Python because we can choose what goes into the body of inner. In this case, we did not perform any type checking on the name or any range allowance on the age, but we could simply add these checks in, by expanding the code defined within the inner function.
Example 4: Using the @property/setter/deleter decorators
Yet another way to control attribute values in Python is through the use of the property decorator and its corresponding setter and deleter methods.
Let’s create our Employees class once more. When the user attempts to access the name attribute through the object.attribute syntax, the name method decorated with @property will be called, and the name will be returned capitalised.
This time if the user would like to change the name attribute, let’s ensure whatever string they input will be capitalised. We can achieve this by defining a simple name method alongside corresponding setter and deleter methods.
We define the name method and decorate it with @name.setter. When the user wants to set a new name, using the object.attribute syntax, the new name set in the object will now be capitalised.
Note, the underlying name of the name attribute ‘_name’, is different to the name of the method to avoid recursive errors. We could not have self.name = new_name.title() in the body of our setter method.
Example 5: Conditional checking in the init Constructor
To finish this article on attribute control, the simplest way to implement attribute integrity, may in fact be to include conditional checking in the init constructor of our class itself. Here, we allow the age of the employee to be greater than or equal to 18, and less than or equal to 100. If the condition is satisfied, we can set the age supplied as an argument in the init method as an attribute value in our object. When an instance is created that fails to satisfy this condition, an AttributeError exception is raised, with a useful string message informing the user of the conditions required.
Thanks for reading, and I hoped you liked the different ways to control attribute access and their values. While I have mainly focused on data-type checking and range allowance, any conditional imaginable can be imposed on an attribute value. An email address may undergo validation using regex before being set, to comply with data integrity.
I have deliberately used basic examples here to help convey how the multiple ways to control attribute values and access work. Furthermore, Descriptors are another way to manage attributes in Python, and I will hopefully write a tutorial piece on how to use them. If you have any questions, or would like more articles themed around this topic, let me know via LinkedIn.