Get AI summaries of any video or article — Sign up free
Python OOP Tutorial 6: Property Decorators - Getters, Setters, and Deleters thumbnail

Python OOP Tutorial 6: Property Decorators - Getters, Setters, and Deleters

Corey Schafer·
4 min read

Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Use `@property` to expose a computed value (like `email`) through attribute syntax while recalculating from current underlying fields.

Briefing

Property decorators in Python let class attributes behave like computed fields—readable like normal attributes, while still backed by methods that can calculate values on demand. The practical payoff shows up immediately with an `Employee` example where `email` depends on `first_name` and `last_name`. After changing `first_name`, the stored `email` becomes stale, even though a `full_name` method stays correct by recomputing from the latest names each time it runs.

A naive fix—turning `email` into a method—would keep the value up to date, but it forces every existing caller to change from `employee.email` to `employee.email()` (adding parentheses). That breaks compatibility with code written against the original attribute-style API. The property decorator solves this by allowing a method to be accessed using attribute syntax. By defining an `email` method and decorating it with `@property`, the code can keep calling `employee.email` while the method still recomputes `first_name + '.' + last_name + '@email.com'` every time it’s accessed.

The tutorial then extends the same idea to assignment. With only `@property`, attempting to set `employee.full_name = 'Quarry Schaefer'` raises an error because properties are read-only unless a setter is added. The fix is another decorator: `@full_name.setter`. That setter method uses the incoming string, splits it on the space into first and last components, and writes them back to `self.first` and `self.last`. Once those underlying fields update, dependent computed properties like `email` automatically reflect the new names.

Finally, a deleter demonstrates cleanup behavior. Using `@full_name.deleter`, the code runs custom logic when `del employee.full_name` is called—here, it prints a message and sets `self.first` and `self.last` to `None`. The overall pattern is that `@property` provides attribute-style access for computed values, `@<property>.setter` enables controlled updates through assignment, and `@<property>.deleter` supports custom teardown logic.

The key takeaway is compatibility: done correctly, property decorators let users keep the same attribute access patterns while the class gains getter/setter/deleter functionality internally. The tutorial also warns that changing an existing method into an attribute can be risky if other code already depends on the old calling style, so the benefits come with the need for careful API design.

Cornell Notes

Python’s `@property` decorator turns a method into an attribute-like interface, enabling computed values to stay consistent with underlying data. In the `Employee` example, `email` is derived from `first_name` and `last_name`, so storing it directly causes staleness when names change. Decorating an `email` method with `@property` keeps callers using `employee.email` while recalculating the address on access. To support assignment like `employee.full_name = 'Quarry Schaefer'`, a matching `@full_name.setter` parses the input and updates `first` and `last`. A `@full_name.deleter` runs cleanup code when `del employee.full_name` is called.

Why does `email` become incorrect after changing `first_name` in the initial `Employee` setup?

In the starting design, `email` is set during initialization based on the current `first_name` and `last_name`. When `employee.first_name` is later changed (e.g., from John to Jim), the stored `email` value does not automatically update, so it still reflects the old first name. By contrast, a `full_name` method recomputes from `first_name` and `last_name` each time it’s called, so it stays correct.

Why is converting `email` into a plain method not a drop-in fix?

Turning `email` into a method would require callers to switch from attribute access to method calls—changing `employee.email` into `employee.email()`. That forces updates across all existing code using the class, breaking backward compatibility. The property decorator avoids this by keeping attribute-style access while still using method logic under the hood.

How does `@property` let a method be accessed like an attribute?

A method decorated with `@property` can be referenced without parentheses. In the example, the `email` method is defined to build the string from `self.first` and `self.last`, and `@property` makes `employee.email` return that computed value directly. The same approach can be applied to `full_name`, so `employee.full_name` behaves like an attribute rather than requiring `employee.full_name()`.

What happens when trying to assign to a property that only has `@property`, and how is it fixed?

With only `@property`, the property is read-only. Attempting `employee.full_name = 'Quarry Schaefer'` triggers an error indicating the attribute can’t be set. Adding a setter via `@full_name.setter` defines a method that receives the assigned value, splits it into first and last names, and updates `self.first` and `self.last`, which then makes dependent computed values like `email` update automatically.

How does a deleter work for properties, and what cleanup does the example perform?

A deleter is defined with `@full_name.deleter`. When `del employee.full_name` is executed, the deleter method runs. In the example, it prints a message and sets `self.first` and `self.last` to `None`, effectively clearing the underlying state that drives computed properties.

Review Questions

  1. In the `Employee` example, what specific change prevents `email` from going stale when `first_name` or `last_name` changes?
  2. What error occurs when assigning to a property that only has `@property`, and which decorator resolves it?
  3. How does the `full_name` setter transform the assigned string into updates for `first` and `last`?

Key Points

  1. 1

    Use `@property` to expose a computed value (like `email`) through attribute syntax while recalculating from current underlying fields.

  2. 2

    Avoid breaking existing code by not replacing `employee.email` with `employee.email()`; `@property` preserves the original access pattern.

  3. 3

    Properties are read-only by default; assignment requires adding a setter with `@<property>.setter`.

  4. 4

    A setter can parse assigned input (e.g., splitting a full name on a space) and update the underlying fields that drive other computed properties.

  5. 5

    A deleter with `@<property>.deleter` runs custom cleanup logic when `del instance.<property>` is called.

  6. 6

    Changing a method into an attribute can affect callers, so property-based API changes should be done carefully for compatibility.

Highlights

The tutorial demonstrates that `@property` fixes stale dependent fields without forcing callers to change from attribute access to method calls.
Setting a property requires a dedicated `@<property>.setter`; otherwise Python raises an error because the property is read-only.
The `full_name` setter parses a single string into `first` and `last`, which then automatically updates `email` through recomputation.

Topics

  • Property Decorators
  • Getters
  • Setters
  • Deleters
  • Python OOP

Mentioned