property()

property(fget=None, fset=None, fdel=None, doc=None)
Returns: property object · Updated March 13, 2026 · Built-in Functions
built-in oop encapsulation getter setter

The property() function returns a property attribute that provides controlled access to class attributes. It lets you define getter, setter, and deleter methods for an attribute, enabling encapsulation without requiring the @property decorator. Properties implement Python’s descriptor protocol under the hood.

Syntax

property(fget=None, fset=None, fdel=None, doc=None)

Parameters

ParameterTypeDefaultDescription
fgetcallableNoneA function to get the attribute value. Called when the property is accessed.
fsetcallableNoneA function to set the attribute value. Called when assigning to the property.
fdelcallableNoneA function to delete the attribute value. Called when del is used on the property.
docstrNoneThe docstring for the property. If omitted, inherits from fget’s docstring if available.

Examples

Read-only property

This example creates a property with only a getter, making the attribute read-only:

class Temperature:
    def __init__(self):
        self._celsius = 0
    
    def get_celsius(self):
        return self._celsius
    
    celsius = property(get_celsius)

t = Temperature()
print(t.celsius)
# 0

# Assigning raises AttributeError since there is no setter
# t.celsius = 25

Full property with getter, setter, and deleter

This example shows all three methods in action:

class Person:
    def __init__(self, name):
        self._name = name
    
    def get_name(self):
        return self._name
    
    def set_name(self, value):
        if not value:
            raise ValueError("Name cannot be empty")
        self._name = value
    
    def del_name(self):
        print(f"Deleting {self._name}")
        del self._name
    
    name = property(get_name, set_name, del_name, "The person's name")

p = Person("Alice")
print(p.name)
# Alice

p.name = "Bob"
print(p.name)
# Bob

del p.name
# Deleting Bob

Note that accessing p.name after deletion raises an AttributeError because the underlying _name attribute no longer exists.

Computed property

Properties can compute values on the fly:

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height
    
    def get_area(self):
        return self.width * self.height
    
    area = property(get_area)

r = Rectangle(5, 3)
print(r.area)
# 15

Common Patterns

Validation in setters

Setters are useful for validating data before storing it:

class Account:
    def __init__(self, balance):
        self.balance = balance
    
    def get_balance(self):
        return self._balance
    
    def set_balance(self, value):
        if value < 0:
            raise ValueError("Balance cannot be negative")
        self._balance = value
    
    balance = property(get_balance, set_balance)

account = Account(100)
account.balance = 50
print(account.balance)
# 50

account.balance = -10
# Traceback (most recent call last):
#   ValueError: Balance cannot be negative

Lazy computation

A property can delay expensive computation until the value is actually needed:

class LazyLoader:
    def __init__(self):
        self._data = None
    
    @property
    def data(self):
        if self._data is None:
            print("Loading data...")
            self._data = [1, 2, 3]
        return self._data

loader = LazyLoader()
print("Created")
print(loader.data)
# Loading data...
# [1, 2, 3]
print(loader.data)
# [1, 2, 3]

See Also