Symbol @ in Python

In Python, the “at symbol” (@) is used in two different contexts:

  • Decorators.
  • Matrix multiplication.

Let’s take a more in-depth look at both of these.

Decorators in Python

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.

Here is a demonstration of how a decorator works in Python:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def extend_behavior(func):
# Create an updated version of 'func'
return func
@extend_behavior
def some_func():
pass
def extend_behavior(func): # Create an updated version of 'func' return func @extend_behavior def some_func(): pass
def extend_behavior(func):
   # Create an updated version of 'func'
   return func

@extend_behavior
def some_func():
    pass

Here the extend_behavior decorator function extends the behavior of some_func. (However, in this simplified example, the extend_behavior only returns the original version of some_func.)

Notice how syntactically it looks beautiful and the intention of extending a function is clear.

However, it is important to notice how the above code works this way under the hood:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def extend_behavior(func):
return func
def some_func():
pass
some_func = extend_behavior(some_func)
def extend_behavior(func): return func def some_func(): pass some_func = extend_behavior(some_func)
def extend_behavior(func):
    return func

def some_func():
    pass

some_func = extend_behavior(some_func)

So whenever you use a decorator, you actually pass a function into another function that returns a new version of it. Then you assign the new function to the original one.

A Practical Example of Decorators in Python

Let’s say you have a function that divides two numbers x and y:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def divide(x, y):
return x / y
def divide(x, y): return x / y
def divide(x, y):
    return x / y

The problem with this function is nothing prevents y from being 0.

You could obviously solve this problem with an if check. But for the sake of demonstration, let’s fix the issue using a decorator.

Let’s create a decorator called guard_zero():

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
def guard_zero(operate):
def inner(x, y):
if y == 0:
print("Cannot divide by 0.")
return
return operate(x, y)
return inner
def guard_zero(operate): def inner(x, y): if y == 0: print("Cannot divide by 0.") return return operate(x, y) return inner
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:

  • Takes a function operate() as an argument. This is the function that we are extending.
  • Extends the function operate() by creating an inner function with the extended behavior.
  • It 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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@guard_zero
def divide(x, y):
return x / y
@guard_zero def divide(x, y): return x / y
@guard_zero
def divide(x, y):
    return x / y

Let’s now test our original divide function:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
print(divide(5, 0))
print(divide(5, 2))
print(divide(5, 0)) print(divide(5, 2))
print(divide(5, 0))
print(divide(5, 2))

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Cannot divide by 0.
None
2.5
Cannot divide by 0. None 2.5
Cannot divide by 0.
None
2.5

As you can see, the updated version of the divide function now makes sure no divisions are made by 0.

(In case you are wondering, the None output is caused by the guard_zero decorator returning None when y is 0.)

Cool! Now you know what is a decorator function in Python and how you can use one. Next, let’s take a look at some common built-in decorators you are going to see.

Useful Decorators in Python

There’s a lot of decorators in Python you are going to see, but the most common ones are:

  • @property
  • @classmethod
  • @staticmethod

Let’s go through each with an example.

@property Decorator in Python

When you mark a method with @property in Python, it is possible to call that method as if it was a property of that class. In other words, you don’t need to use parenthesis to call the method:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
weight.pounds() ---> weight.pounds
weight.pounds() ---> weight.pounds
weight.pounds() ---> weight.pounds

A method marked as @property is also known as a getter method.

Let’s see an example.

Say you have a class called Mass that stores weight both in kilos and pounds:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Mass:
def __init__(self, kilos):
self.kilos = kilos
self.pounds = kilos * 2.205
class Mass: def __init__(self, kilos): self.kilos = kilos self.pounds = kilos * 2.205
class Mass:
    def __init__(self, kilos):
        self.kilos = kilos
        self.pounds = kilos * 2.205

You can use this class by:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mass = Mass(100)
print(mass.kilos)
print(mass.pounds)
mass = Mass(100) print(mass.kilos) print(mass.pounds)
mass = Mass(100)

print(mass.kilos)
print(mass.pounds)

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
100
220.5
100 220.5
100
220.5

But now, let’s modify the number of kilos of the mass object. Pay attention to what happens to the pounds property.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mass.kilos = 10000000
print(mass.pounds)
mass.kilos = 10000000 print(mass.pounds)
mass.kilos = 10000000
print(mass.pounds)

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
220.5
220.5
220.5

Yup, it remains the same that it started. But this is not a surprise as we did not modify the pounds. We only touched kilos.

One way to fix the problem is to not store pounds as a separate variable in the object. Instead, you can write a method that converts the current number of kilos to pounds:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Mass:
def __init__(self, kilos):
self.kilos = kilos
def pounds(self):
return self.kilos * 2.205
class Mass: def __init__(self, kilos): self.kilos = kilos def pounds(self): return self.kilos * 2.205
class Mass:
    def __init__(self, kilos):
        self.kilos = kilos
            
    def pounds(self):
        return self.kilos * 2.205

Now let’s see when we create a Mass object and update its weight in kilograms:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mass = Mass(100)
print(mass.pounds())
mass.kilos = 500
print(mass.pounds())
mass = Mass(100) print(mass.pounds()) mass.kilos = 500 print(mass.pounds())
mass = Mass(100)
print(mass.pounds())

mass.kilos = 500
print(mass.pounds())

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
220.5
1102.5
220.5 1102.5
220.5
1102.5

Now the weight is always correct in both pounds and kilos.

But as a consequence, you can no longer call mass.pounds without parenthesis, as pounds() is now a method. This change breaks the code if you still call mass.pounds somewhere.

This is where the @property decorator helps you.

If you mark the pounds() method with @property decorator, you can call mass.pounds without parenthesis again:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Mass:
def __init__(self, kilos):
self.kilos = kilos
@property
def pounds(self):
return self.kilos * 2.205
class Mass: def __init__(self, kilos): self.kilos = kilos @property def pounds(self): return self.kilos * 2.205
class Mass:
    def __init__(self, kilos):
        self.kilos = kilos
        
    @property
    def pounds(self):
        return self.kilos * 2.205

Example call:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
mass = Mass(100)
print(mass.pounds)
mass.kilos = 500
print(mass.pounds)
mass = Mass(100) print(mass.pounds) mass.kilos = 500 print(mass.pounds)
mass = Mass(100)
print(mass.pounds)

mass.kilos = 500
print(mass.pounds)

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
220.5
1102.5
220.5 1102.5
220.5
1102.5

The decorated pounds() method is now called a getter method. It does not store the number of pounds in the object while syntactically it looks as if it did.

@classmethod Decorator in Python

A class method is useful when you need a method that involves the class and 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:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Weight:
def __init__(self, kilos):
self.kilos = kilos
class Weight: def __init__(self, kilos): self.kilos = kilos
class Weight:
    def __init__(self, kilos):
        self.kilos = kilos

You can create Weight objects like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
w1 = Weight(100)
w1 = Weight(100)
w1 = Weight(100)

But if you want to create a Weight object from pounds, you have to convert pounds to kilos in advance:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
pounds = 220.5
kilos = pounds / 2.205
w2 = Weight(kilos)
print(w2.kilos)
pounds = 220.5 kilos = pounds / 2.205 w2 = Weight(kilos) print(w2.kilos)
pounds = 220.5
kilos = pounds / 2.205

w2 = Weight(kilos)
print(w2.kilos)

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
100
100
100

So you always need to remember to convert pounds to kilos before creating a Weight. This is bad practice.

What if you could create a Weight object from pounds directly like this?

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
w2 = Weight.from_pounds(500)
w2 = Weight.from_pounds(500)
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”:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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)
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)
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)

Now it is indeed possible to create a Weight object directly from pounds using the second initializer:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
w2 = Weight.from_pounds(220.5)
print(w2.kilos)
w2 = Weight.from_pounds(220.5) print(w2.kilos)
w2 = Weight.from_pounds(220.5)
print(w2.kilos)

And the number of kilos is also automatically correct:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
100
100
100

Let’s take a look at how the from_pounds class method works more in-depth:

  • The @classmethod marks the from_pounds() as a classmethod.
  • The first argument cls is a mandatory argument. It’s similar to self in a regular method. The difference is that cls represents the whole Weight class, not an instance of it.
  • Inside the actual from_pounds() method, the pounds are converted to kilos and a new Weight object is returned.
  • (Here the return cls(kilos) is the same as return Weight(kilos).)

So the class method takes a number of pounds as an argument, converts it to kilos, and returns a new Weight object.

@staticmethod Decorator in Python

A static method is tied to the class, not to the instance of it.

A static method reminds a class method. But the key difference is that a static method doesn’t modify the class at all. This means a static method does not accept self nor cls as arguments either.

A static method is commonly used as a utility related to the class.

For instance, let’s continue with the Weight class. Let’s add a static method conversion_info() to tell how kilos are converted to pounds:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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.")
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.")
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 call this utility method:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Weight.conversion_info()
Weight.conversion_info()
Weight.conversion_info()

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Kilos are converted to pounds by multiplying by 2.205.
Kilos are converted to pounds by multiplying by 2.205.
Kilos are converted to pounds by multiplying by 2.205.

The point of a static method is that no matter what values the object has, it always produces the same result. For example, the conversion factor 2.205 is the same regardless of the number of kilos.

Remember you can also accept arguments in static methods if needed.

Matrix Multiplication

Since Python 3.5, it’s been possible to use @ symbol to multiply matrixes.

This of course means you need to have a Matrix class that implements the matrix multiplication algorithm in the __matmul__ method.

For example, let’s create a Matrix class, and implement the __matmul__ for matrix multiplication:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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))])
# Example matrices
A = Matrix([[1, 2],[3, 4]])
B = Matrix([[5, 6],[7, 8]])
# Example of multiplying matrices
print(A @ B)
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))]) # Example matrices A = Matrix([[1, 2],[3, 4]]) B = Matrix([[5, 6],[7, 8]]) # Example of multiplying matrices print(A @ B)
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))])

# Example matrices
A = Matrix([[1, 2],[3, 4]])
B = Matrix([[5, 6],[7, 8]])


# Example of multiplying matrices
print(A @ B)

Output:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
[[19, 22], [43, 50]]
[[19, 22], [43, 50]]
[[19, 22], [43, 50]]

As the point of this guide is to show how to use @ in Python, I’m not going into further details about matrix multiplication. In case you are interested to learn more about matrix multiplication, check this article that explains the algorithm.

Conclusion

Today you learned how the at symbol @ is used in Python.

To recap, the main use for the @ symbol is decorators. A decorator is used to extend the functionality of a function, method, or class from outside.

Common decorators you are going to see are:

  • @property. Turns a method in a class into a getter method that can be called without parenthesis.
  • @classmethod. Turns a method in a class to a classmethod that is not tied to an object, but to the class instead. This is commonly used to create a second initializer to a class.
  • @staticmethod. A class-independent method. Does not modify the class objects but is related to the class in a way that makes sense to add it ther.

Another use case for @ symbol is matrix multiplication. To support this, you need to implement a Matrix class with the __matmul__ method.

Thanks for reading.

Happy coding!

Further Reading

50 Python Interview Questions