All about Python decorators
Originally posted on iCode
If you’re new to Python or never worked with traditional languages such as Java, at some point you’ve most likely opened a piece of Python code only to be bewildered by the strange “@” notation. This is the Python decorator syntax. In this article, I will demystify the Python decorator and explain its many use cases.
In brief, the decorator syntax allows the extension of a method with a decorator function without modifying the structure of the original function. Decorators can be as simple as a function that logs the input and output of a function to better facilitate debugging or a dominant feature of a Framework modifying how the functions themselves execute (such as in the Web Framework Flask).
How Do They Work?
Decorators essentially wrap your method inside another function, a very simple example would be if you had a function that returned a string and you needed that string to be upper case.
How Are Function Arguments Passed?
Function arguments are passed directly to the wrapper function, if you need to use an unknown or varying number of arguments then *args and **kwargs can be used. For example, a generic logging decorator would need to accept a varying number of arguments. Below we print out the incoming arguments before calling the sum_values method, then store and print the output of the method.
Getters and Setters
Getters and setters allow you to encapsulate the behaviour of properties within a class, allowing for additional functionality such as validation of a property on change. Traditionally this is done with two functions, normally named get_property and set_property. However, Python has an elegant solution to this, the property and setter decorators.
These two decorators allow for getter and setter functionality on a property while hiding the get and set methods. Instead, the getter and setter methods are named the desired property name and depending on how the property is used (get or set), the appropriate method is called.
In the example below of the class Heater, there is an internal property __temperature, which is encapsulated within the @property and @temperature.setter. If the property temperature is used in a read context then the @property method is called, and if it’s used in a write context then the @temperature.setter method is called. Here on set, we add extra validation so that the temperature cannot be set to a non-physical value.
Class Methods and Static Methods
Python has built-in decorators to support class methods and static methods. These allow access to methods with or without instantiation of the class. A class method receives the class as the first argument, just like a normal method within a class receives the instance (self) as the first argument. This is useful if you need to access the method without instantiating the class, but still need access to the class variables.
A static method is similar to a class method however, it has no access to the class or the instance and has no required arguments. This is useful if you need access to the method outside the class without instantiating it and have no requirement on any class properties.
Below is an example class containing a class method and a static method.
Summary and Further Reading
Decorators allow you to modify the behaviour of a method without modifying its structure. This helps in keeping your code clean and easy to maintain without compromising on functionality.
They can greatly simplify the refactoring of legacy code, as you can quickly extend the functionality of the existing code, without needing to modify the methods themselves.
If you’re interested in the origin of decorators in Python PEP-0318 contains a discussion and introduction of decorators into the Python language.