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)
PythonOutput:
Traceback (most recent call last):
File "oop4.py", line 14, in <module>
print(product.__price)
AttributeError: 'Product' object has no attribute '__price'
PlaintextIf 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)
PythonOutput:
99.99
99.99
PlaintextBut 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
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)
PythonNOTES
- 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
PlaintextSetter
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)
PythonNOTES
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
PlaintextDeleter
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)
PythonNOTES
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
PlaintextWARNING
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'
Plaintextproperty 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)
PythonWARNING
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.
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)
PythonOutput:
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