• Python

Command Palette

Search for a command to run...

Python Fundamentals
  • Python Variables
  • Python Operators
  • Python Input-Output
  • Python Type Conversion
Python Data Types
  • Python Strings
  • Python List
  • Python Tuple
  • Python Dictionnaries
  • Python Sets
Python Flow Control
  • Python Conditions
  • Python For Loop
  • Python While Loop
  • Python Break and Continue
Python Functions
  • Python Functions
  • Python Arguments
  • Python Functions Scope
  • Python Recursion
Python Classes
  • Python Classes
  • Python Classes and Static Methods
  • Python Properties
  • Python Decorators
  • Python Error Handling

Create an Account

FREE

Join our community to access more courses.

Create Account

On this page

Controlling Attribute Access: Understanding PropertiesWhat Is a Property?Getters and Setters Without PropertiesWhat Are Getters and Setters?Defining a Property with @propertyAdding a Setter to a PropertyWhy Use Properties?Defining Properties the Old Way (property())Common Beginner MistakesSummaryAssignment: Practice Time!
      • Pricing
      • Blog

      Python Properties

      Controlling Attribute Access: Understanding Properties

      In Python, you often want to control how the data (attributes) of your objects are accessed or changed. Sometimes you want to add logic when getting or setting a value, like checking for valid data or updating something else automatically. This is where properties come in.

      This article will show you what properties are, why they are useful, and how to use them in your own classes.

      What Is a Property?

      A property lets you define special methods in a class that are used when you get, set, or delete an attribute. Properties make it possible to add logic to attribute access, while still letting you use simple dot notation (obj.value).

      Properties are most often used to:

      • Validate data before setting it
      • Make an attribute read-only
      • Compute a value on the fly
      • Run code when an attribute is changed

      Getters and Setters Without Properties

      Before properties, the common way to control access to an attribute in Python was to write explicit methods for getting and setting the value. These are usually named get_attribute and set_attribute.

      Example:

      class Person:
          def __init__(self, name: str) -> None:
              self._name: str = name
      
          def get_name(self) -> str:
              return self._name
      
          def set_name(self, value: str) -> None:
              if not value:
                  raise ValueError("Name cannot be empty!")
              self._name = value
      
      p = Person("Alice")
      print(p.get_name())  # Output: Alice
      p.set_name("Bob")   # Works fine
      # p.set_name("")    # Raises ValueError
      
      • You have to call get_name() and set_name() instead of using p.name and p.name = ....

      Problems with this approach:

      • The code is less readable and less Pythonic. You can't use simple attribute access.
      • If you change your class to use get/set methods, all code using your class must also change.
      • You lose the simplicity and clarity of dot notation (p.name).
      • It's easy to forget to use the methods and accidentally access the attribute directly.

      Properties solve these problems by letting you add logic to attribute access while keeping the same simple syntax.

      What Are Getters and Setters?

      A getter is a method that is used to read (get) the value of an attribute. It lets you control what happens when someone accesses the attribute, such as returning a computed value or adding a print statement for debugging.

      A setter is a method that is used to set (change) the value of an attribute. It lets you control what happens when someone assigns a new value, such as checking if the value is valid or updating related data.

      • The getter is called automatically when you write value = obj.attribute.
      • The setter is called automatically when you write obj.attribute = new_value.

      With properties, you can define both a getter and a setter for the same attribute name, so you can add logic to both reading and writing the value, while still using simple attribute access.

      Defining a Property with @property

      The easiest way to create a property is with the @property decorator. You define a method with @property to make it act like a getter (for reading the value). You can also define setter and deleter methods for writing or deleting the value.

      Example: Making an Attribute Read-Only

      class Circle:
          def __init__(self, radius: float) -> None:
              self._radius: float = radius
      
          @property
          def radius(self) -> float:
              return self._radius
      
          @property
          def diameter(self) -> float:
              return self._radius * 2
      
      c = Circle(5.0)
      print(c.radius)    # Output: 5.0
      print(c.diameter)  # Output: 10.0
      # c.radius = 10.0  # ERROR: can't set attribute (no setter defined)
      
      • radius and diameter are properties. You use them like attributes, but they are actually methods.
      • radius is read-only because there is no setter.

      Adding a Setter to a Property

      If you want to allow changing the value, add a setter method using @property_name.setter.

      Example: Validating Data When Setting

      class Person:
          def __init__(self, name: str) -> None:
              self._name: str = name
      
          @property
          def name(self) -> str:
              return self._name
      
          @name.setter
          def name(self, value: str) -> None:
              if not value:
                  raise ValueError("Name cannot be empty!")
              self._name = value
      
      p = Person("Alice")
      print(p.name)   # Output: Alice
      p.name = "Bob" # Works fine
      # p.name = ""   # Raises ValueError
      
      • The setter lets you add checks before changing the value.

      Why Use Properties?

      • Encapsulation: Hide the internal details of how data is stored.
      • Validation: Check or clean data before setting it.
      • Read-only or computed attributes: Make some values read-only, or calculate them on the fly.
      • Backward compatibility: Change how an attribute works without changing how you use it in code.

      Defining Properties the Old Way (property())

      You can also create properties using the built-in property() function. This is less common now, but you may see it in older code.

      from typing import Any
      
      def get_x(self) -> Any:
          return self._x
      
      def set_x(self, value: Any) -> None:
          self._x = value
      
      class Example:
          x = property(get_x, set_x)
      

      Common Beginner Mistakes

      • Forgetting to use an underscore (like _value) for the real data, causing infinite recursion.
      • Not defining a setter when you want to allow changing the value.
      • Trying to set a read-only property (will raise an error).

      Example of a mistake:

      class Bad:
          @property
          def value(self) -> Any: # Added type hint
              return self.value  # ERROR: calls itself forever!
      

      Summary

      • Use @property to control how attributes are accessed or changed.
      • Properties let you add logic to getting, setting, or deleting attributes.
      • Use a setter (@name.setter) to allow changing the value.
      • Properties help with validation, encapsulation, and computed values.

      Assignment: Practice Time!

      1. Create a class Temperature with:

      • An attribute for Celsius (store as _celsius).
      • A property celsius for getting and setting the value (raise an error if below -273.15).
      • A read-only property fahrenheit that returns the temperature in Fahrenheit.

      2. Try creating a temperature, changing it, and printing both Celsius and Fahrenheit.

      class Temperature:
          def __init__(self, celsius: float) -> None:
              self.celsius = celsius # Use the setter for initial validation
      
          @property
          def celsius(self) -> float:
              return self._celsius
      
          @celsius.setter
          def celsius(self, value: float) -> None:
              if value < -273.15:
                  raise ValueError("Temperature cannot be below absolute zero (-273.15 C)!")
              self._celsius: float = value
      
          @property
          def fahrenheit(self) -> float:
              return (self.celsius * 9/5) + 32
      
      # Example Usage:
      temp = Temperature(25.0)
      print(f"{temp.celsius}°C is {temp.fahrenheit}°F")
      
      temp.celsius = 100.0
      print(f"{temp.celsius}°C is {temp.fahrenheit}°F")
      
      # This would raise a ValueError:
      # temp.celsius = -300.0
      

      Continue Learning

      Python Variables

      Popular

      Getting Started: Understanding Variables in Python In programming, we often need to store informatio

      Python Decorators

      For You

      Adding Functionality to Functions: Understanding Decorators In Python, you might want to add extra f

      Python Arguments

      For You

      Providing Input to Functions In the previous article, we learned that functions are reusable blocks

      Personalized Recommendations

      Log in to get more relevant recommendations based on your reading history.