Python: Getter, Setter, and Deleter

Getters and setters work as the entry point for managing and manipulating data for an object of a class. In this article, we are discussing the getter, setter, and deleter for the property of a Python class.

For example, we have a Product class, which has a property for storing price. We are making the property private to avoid direct access to the property.

Name of the property of __price. If we try to access the property directly, then we get an error.

class Product:
    def __init__(self, price):
        self.__price = price
        
# Usage exmple
product = Product(99.99)

print(product.__price)
Python

Output:

Traceback (most recent call last):
  File "oop4.py", line 14, in <module>
    print(product.__price)
AttributeError: 'Product' object has no attribute '__price'
Plaintext

If we want to normally access the __price property then we can do it in the following ways-

  • One way is to create a function and return the __price from that. Like, here we have declared a function named getPrice, that returns the __price.
  • Prefix the property named with an underscore(_) and then the class name. In this case, we have to prefix it with _Product. so we can access _Product__price from an object of the class.
# Following code is for demo purpose only
# These are not the recommended way to get a private property

class Product:
    def __init__(self, price):
        self.__price = price
        
    # This is not the recommended way
    def getPrice(self):
        return self.__price



# Usage exmple
product = Product(99.99)

print(product.getPrice())

# This is not the recommended way
print(product._Product__price)
Python

Output:

99.99

99.99
Plaintext

But there is a better way to do this. We can use the @property annotation for the getter, and other annotations for the setter and deleter.

NOTES

  • Getter, setter, and deleter functions can have the same name. The decorators differentiate between the usability and purpose of the function.
  • Python getter, setter, and deleter will work for any type of property, with any access.

Let’s check one by one-

Getter

Add a decorator @property to the getter function.
Define a getter function. It can be of any name, but it is better to name it related to the property name.
Return the property value from the function.
We can use the getter by just the function name of the getter.
class Product:
    def __init__(self, price: float) -> None:
        self.__price: float = price

    @property
    def price(self) -> float:
        return self.__price


# Usage example
product = Product(99.99)

# get price using getter
print(product.price)
Python

NOTES

  • Parenthesis is not required while accessing the value through the getter.
  • The getter function can be named anything(as long as it follows function naming rules).

Output:

99.99
Plaintext

Setter

Use decorator in the format @the_getter_name.setter. The part after the @ sign should match the name of the getter function.
Define the setter function.
Accept the value as a param in the function, and set the value to the class property we targeted for this setter.
While using the setter we need to assign the value in the format – object_name.setter_function_name = our_value.
class Product:
    def __init__(self, price: float) -> None:
        self.__price: float = price

    @property
    def price(self) -> float:
        return self.__price

    @price.setter
    def price(self, value: float) -> None:
        if value < 0:
            raise ValueError("Price cannot be negative")
        self.__price = value

# Usage example
product = Product(99.99)

# Set price using setter
product.price = 70.0

print(product.price)
Python

NOTES

The name of the setter function can be anything(following the function naming rules).

We just need to make sure that we use the same name while setting the value.

Output:

70
Plaintext

Deleter

Use decorator in the format @the_getter_name.deleter. The part after the @ sign should match the getter name.
Define a function for deleting.
In the function delete the value with “del”.
To delete the value, use “del object_name.deleter_name“.
class Product:
    def __init__(self, price: float) -> None:
        self.__price: float = price

    @property
    def price(self) -> float:
        if hasattr(self, '_Product__price'):
            return self.__price
        else:
            raise AttributeError("Price has been deleted")
    
    @price.setter
    def price(self, value: float) -> None:
        if value < 0:
            raise ValueError("Price cannot be negative")
        self.__price = value
        
    @price.deleter
    def price(self) -> None:
        del self.__price

# Usage example
product = Product(99.99)

print(product.price)

# Delete price using the deleter
del product.price

try:
    print(product.price)
except AttributeError as e:
    print(e)
Python

NOTES

The deleter function name can be anything. Just make sure that you use the same name while using the deleter.

Output:

99.99
Price has been deleted
Plaintext

WARNING

If exception is not handled(like in the above code) which accessing the price property, and the property is deleted then we will get the following error-

Traceback (most recent call last):
  File "oop4.py", line 27, in <module>
    print(product.price)
  File "oop4.py", line 7, in price
    return self.__price
AttributeError: 'Product' object has no attribute '_Product__price'
Plaintext

property Decorator Definition

Here is what the property decorator looks like-

class property(object):

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        if doc is None and fget is not None and hasattr(fget, "__doc__"):
            doc = fget.__doc__
        self.__get = fget
        self.__set = fset
        self.__del = fdel
        try:
            self.__doc__ = doc
        except AttributeError:  # read-only or dict-less class
            pass
        self.__name = None

    def __set_name__(self, owner, name):
        self.__name = name

    @property
    def __name__(self):
        return self.__name if self.__name is not None else self.fget.__name__

    @__name__.setter
    def __name__(self, value):
        self.__name = value

    def __get__(self, inst, type=None):
        if inst is None:
            return self
        if self.__get is None:
            raise AttributeError("property has no getter")
        return self.__get(inst)

    def __set__(self, inst, value):
        if self.__set is None:
            raise AttributeError("property has no setter")
        return self.__set(inst, value)

    def __delete__(self, inst):
        if self.__del is None:
            raise AttributeError("property has no deleter")
        return self.__del(inst)
Python

WARNING

This is not the exact definition of the @property decorator. But it reflects how the definition should look like.

Examples

Let’s check a full example of getter, setter, and deleted. Here we have a Product class, that has 3 properties, 2 of which are private.

We have created a getter, setter, and deleted for __price and __quantity.

We have class Product.
__init__() is defined to set/initialize properties.
There is one public property model, and 2 private properties __price, and quantity.
Then we have getter, setter, and deleter for price and quantity.
There is also a getter function for getting total price. which does not represent any attribute directly, but gives us a calculated value.
class Product:
    def __init__(self, model: str, price: float) -> None:
        self.model: str = model
        self.__price: float = price
        self.__quantity: int = 1  # Default to 1 quantity

    @property
    def price(self) -> float:
        return self.__price

    @price.setter
    def price(self, value: float) -> None:
        if value < 0:
            raise ValueError("Price cannot be negative")
        self.__price = value

    @price.deleter
    def price(self) -> None:
        del self.__price

    @property
    def quantity(self) -> int:
        return self.__quantity
    
    @quantity.setter
    def quantity(self, value: int) -> None:
        if value < 0:
            raise AttributeError("Quantity can not be negative")
        self.__quantity = value
    
    @quantity.deleter
    def quantity(self) -> None:
        self.__quantity = 0

    @property
    def total_price(self) -> float:
        return self.__price * self.__quantity


# Usage example
product = Product("SWH-30G21", 99.99)

print(f"Model: {product.model}")

print(f"Total price of {product.quantity} product is {product.total_price}")

product.quantity = 10

print(f"Total price of {product.quantity} products is {product.total_price}")

# Delete quantity
# which set the quantity to zero(as defined in the deleter)
del product.quantity

print(product.quantity)

# Delete price
del product.price

try:
    print(product.price)
except AttributeError as e:
    print(e)
    
# Set the price again
product.price = 800

print(product.price)
Python

Output:

Model: SWH-30G21

Total price of 1 product is 99.99

Total price of 10 products is 999.9

0

'Product' object has no attribute '_Product__price'

800
Plaintext

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.