What Is the @ Symbol in Python and How Do I Use It?

The @ symbol in Python is used to apply a decorator to a function or method to extend its functionality, or to help perform matrix multiplication. Here's what to know about each use case.

Written by Artturi Jalli
python-symbol
Brand Studio Logo
UPDATED BY
Brennan Whitfield | Mar 18, 2025

Have you ever wondered what @property in Python 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.

When to Use the @ Symbol in Python

The main use case of the symbol @ in Python is to apply a decorator to a function or method. In Python, a decorator is a function that extends the functionality of an existing function or class.

 

Python Decorators in 15 Minutes. | Video: Kite

What Is the @ Symbol and What Are Decorators in Python?

The @ symbol in Python is used to apply a decorator to an existing function or method and extend its functionality.

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

RelatedStop Using NumPy’s Global Random Seed

 

Common Decorators in Python

There are many decorators you’ll see, but here are the most common ones. Let’s go through each with an example.

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

@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 constructor or initializer method for a class by using a class method.

To create a class method in Python, decorate it with @classmethod.

Example

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 the from_pounds() as a class method.
  • A class method receives the class (cls) as its first argument instead of an instance (self). cls is similar to self in a method, but the difference is that cls represents the whole Weight class, not just an instance of it.
  • In the method, the pounds are converted to kilos.
  • The returncls(kilos) is the same as return 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

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

Example

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.

RelatedYour Variable Names Are a Mess. Clean Up Your Code.

 

Python @ Symbol and 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!

Frequently Asked Questions

The @ symbol in Python is mainly used to apply decorators to existing functions or methods to modify their behavior. @ is also used for performing matrix multiplication in Python 3.5 and later.

A decorator is a Python function that takes another function as input, extends its behavior, and returns the modified function. The @ symbol is put before the name of a decorator to define it as such (i.e. @decorator_example).

Explore Job Matches.