Have you ever wondered what @property means? Or what A @ B does? I’ll show you how the symbol @ is used in Python.
There are two use cases: decorators and matrix multiplication.
The main use case of the symbol @ in Python is decorators. In Python, a decorator is a function that extends the functionality of an existing function or class.
Decorators
The main use case of the symbol @ in Python are decorators. In Python, a decorator extends the functionality of an existing function or class.
For example, this piece of code . . .
def extend_behavior(func):}
return func
@extend_behavior
def some_func():
pass
. . . does the exact same as this piece of code:
def extend_behavior(func):
return func
def some_func():
Pass
some_func = extend_behavior(some_func)
Decorators: A Practical Example
Let’s say you have a function that divides two numbers:
def divide(x, y):
return x / y
The problem with this function is that nothing prevents y
from being 0
.
You could solve this with an if
check. But for the sake of demonstration, let’s patch the issue using a decorator.
Let’s start by creating a decorator function called guard_zero()
:
def guard_zero(operate):
def inner(x, y):
if y == 0:
print("Cannot divide by 0.")
return
return operate(x, y)
return inner
This decorator . . .
- accepts
operate()
function as an argument. - extends the function
operate()
by creating an inner function with the extended behavior. - returns the
inner()
function—a new version of operate()
function.
Now you can use the decorator guard_zero()
to extend the behavior of divide()
to ensure no divisions with 0
are made:
divide = guard_zero(divide)
This works, but there’s a more conventional approach to applying a decorator:
@guard_zero
def divide(x, y):
return x / y
This makes it more readable and the programmer’s intent becomes clear.
Now you can test the divide()
function with different inputs to see it do what it is supposed to:
print(divide(5, 0))
print(divide(5, 2))
Output:
Cannot divide by 0.
None
2.5
(The None
output originates from the fact that guard_zero()
returns None
when the value y
is 0
.)
Common Decorators
There are many decorators you’ll see, but here are the most common ones.
Common Decorators in Python
- @property
- @classmethod
- @staticmethod
Let’s go through each with an example.
More Tutorials From Built In ExpertsStop Using NumPy’s Global Random Seed
@Property
Tagging a method with @property
makes it possible to access an object’s method like a regular attribute:
weight.pounds() ---> weight.pounds
A property decorator makes the decorated method a getter method.
Example
Say you have a Mass
class that stores the mass in kilos and pounds.
class Mass:
def __init__(self, kilos):
self.kilos = kilos
self.pounds = kilos * 2.205
You can use it by:
mass = Mass(100)
print(mass.kilos)
print(mass.pounds)
Result:
100
220.5
Now, let’s modify the number of kilos
and see what happens to pounds
:
mass.kilos = 1500
print(mass.pounds)
Output:
220.5
The pounds
didn’t change. This happened because we never updated pounds
.
You can fix the issue by replacing the pounds
attribute with a pounds()
method:
class Mass:
def __init__(self, kilos):
self.kilos = kilos
def pounds(self):
return self.kilos * 2.205
Now you may modify the number of kilos
and the pounds
will be correct:
mass = Mass(100)
print(mass.pounds())
mass.kilos = 500
print(mass.pounds())
Output:
220.5
1102.5
Now you can’t call mass.pounds
anymore because pounds()
is a method, not an attribute. If the Mass
class is used somewhere else, the above change breaks the code.
This is where a @property
decorator helps you.
If you mark the pounds()
method with @property
it allows you to call mass.pounds
without parentheses again.
class Mass:
def __init__(self, kilos):
self.kilos = kilos
@property
def pounds(self):
return self.kilos * 2.205
The pounds()
method is now called a getter method. It doesn’t store the number of pounds in the object but computes it by converting kilos to pounds.
Built In Tools for Built In Readers4 Tools to Speed Up Exploratory Data Analysis (EDA) in Python
@Classmethod
A class method is useful when you need a method that involves the class but isn’t instance-specific.
For example, you can create an alternative initializer method for a class by using a class method.
To create a class method in Python, decorate it with @classmethod
.
Use Class Method as an Alternative Constructor
Let’s say you have a Weight
class that instantiates weight objects with kilos:
class Weight:
def __init__(self, kilos):
self.kilos = kilos
You can create Weight
objects like this:
w1 = Weight(100)
But if you want to create a Weight
object from pounds, you have to take care of converting pounds to kilos in advance.
pounds = 220.5
kilos = pounds / 2.205
w2 = Weight(kilos)
print(w2.kilos)
Output:
100
So you always need to remember to convert pounds to kilos before creating a Weight
. This is bad practice.
Wouldn’t something like this be more efficient?
w2 = Weight.from_pounds(500)
To do this, let’s create a class method from_pounds()
that acts as an alternative constructor or a second initializer:
class Weight:
def __init__(self, kilos):
self.kilos = kilos
@classmethod
def from_pounds(cls, pounds):
# convert pounds to kilos
kilos = pounds / 2.205
# cls is the same as Weight. calling cls(kilos) is the same as Weight(kilos)
return cls(kilos)
Let’s see how this class method works:
- The
@classmethod
marks thefrom_pounds()
as a class method. - The first argument
cls
is a mandatory argument on a class method. It’s similar to self in a method. The difference is thatcls
represents the wholeWeight
class, not just an instance of it. - In the method, the
pounds
are converted tokilos
. - The return
cls(kilos)
is the same asreturn Weight(kilos)
.
Simply put, this class method takes a number of pounds
as an argument, converts it to kilos
and returns a new Weight
object.
Now it’s possible to do:
w2 = Weight.from_pounds(220.5)
print(w2.kilos)
Output:
100
More From Our Built In Engineering ExpertsCreate React App and TypeScript — A Quick How-To
@Staticmethod
A static method is tied to the class, not to its instance. This may remind you of a class method but the key difference is that a static method doesn’t modify the class at all. In other words, a static method doesn’t take self
or cls
as its arguments.
We most often use a static method as a utility related to the class.
Let’s continue with the Weight
class. Start by adding a static method conversion_info()
to tell how kilos are converted to pounds:
class Weight:
def __init__(self, kilos):
self.kilos = kilos
@classmethod
def from_pounds(cls, pounds):
# convert pounds to kilos
kilos = pounds / 2.205
# cls is the same as Weight. calling cls(kilos) is the same as Weight(kilos)
return cls(kilos)
@staticmethod
def conversion_info():
print("Kilos are converted to pounds by multiplying by 2.205.")
Now you can use this utility to print how the conversion happens:
Weight.conversion_info()
Result:
Kilos are converted to pounds by multiplying by 2.205.
Your Mom Doesn’t Work HereYour Variable Names Are a Mess. Clean Up Your Code.
Matrix Multiplication
Since Python 3.5, it’s been possible to use @
to multiply matrices.
For example, let’s create a matrix class, and implement the __matmul__()
method for matrix multiplication:
class Matrix(list):
def __matmul__(self, B):
A = self
return Matrix([[sum(A[i][k] * B[k][j] for k in range(len(B)))
for j in range(len(B[0])) ] for i in range(len(A))])
A = Matrix([[1, 2],[3, 4]])
B = Matrix([[5, 6],[7, 8]])
print(A @ B)
Output:
[[19, 22], [43, 50]]
There you have it: the @
symbol in Python and how you can use it to clean up your code. Happy coding!
Author: Benjamin Jones
Last Updated: 1703676602
Views: 1125
Rating: 4.1 / 5 (50 voted)
Reviews: 94% of readers found this page helpful
Name: Benjamin Jones
Birthday: 2016-02-29
Address: PSC 2357, Box 2249, APO AA 45675
Phone: +3623243988907535
Job: Article Writer
Hobby: Cross-Stitching, Fishing, Skydiving, Survival Skills, Drone Flying, Chess, Scuba Diving
Introduction: My name is Benjamin Jones, I am a dedicated, brilliant, ingenious, exquisite, irreplaceable, forthright, dear person who loves writing and wants to share my knowledge and understanding with you.