Python Properties
What is a Property?
A property is a special attribute that lets you control what happens when an attribute is accessed or modified. It combines a getter and setter behind a clean, attribute-style interface — no need to call methods explicitly.
The Problem Properties Solve
By default, any attribute can be changed freely from outside a class:
class Person:
def __init__(self, name):
self.name = name
self.salary = 0
p = Person("Tim")
p.salary = -100 # Nothing stops this — but negative salary makes no sense!
Private Attributes
To signal that an attribute shouldn't be touched from outside the class, prefix it with an underscore:
self._salary = 0
- This is a convention, not a true restriction — Python doesn't enforce it
- The underscore means: "treat this as private; don't access it directly outside the class"
- The opposite, a public attribute, has no underscore and is freely accessible
Naming Classes (Bonus Tip from the Lesson)
- Use PascalCase:
Person,BankAccount - Name should be singular — every instance is one of whatever the class is named
- ❌
Persons— instances aren't multiple people, they're one person - ✅
Person— just like Python'sint, notints
Setting Up a Property — Two Ways
Method 1: Legacy (property() function)
class Person:
def __init__(self, name):
self.name = name
self._salary = 0
def get_salary(self):
return round(self._salary)
def set_salary(self, salary):
if salary < 0:
raise ValueError("Hey, this is invalid!")
self._salary = salary
salary = property(get_salary, set_salary)
- Pass the getter first, then the setter
- The property name (
salary) is what you use to access the attribute
Method 2: Decorator (Preferred ✅)
class Person:
def __init__(self, name):
self.name = name
self._salary = 0
@property
def salary(self): # getter — named after the property
return round(self._salary)
@salary.setter
def salary(self, salary): # setter — same name, decorated with @<name>.setter
if salary < 0:
raise ValueError("Hey, this is invalid!")
self._salary = salary
Rules for the decorator approach:
- @property decorates the getter
- @<property_name>.setter decorates the setter
- Both methods must have the same name (the property name)
- The getter must be defined above the setter
- The underlying stored value must use a private attribute (e.g. _salary) — using the same name causes infinite recursion
Full Example — Time Class
class Time:
def __init__(self, second):
self._second = second # uses the setter implicitly? No — direct assignment here
@property
def second(self):
return self._second
@second.setter
def second(self, second):
if second < 0 or second > 60:
raise ValueError("Invalid!")
self._second = second
t = Time(54)
t.second = 100 # raises ValueError: Invalid!
t.second = 59 # works fine
print(t.second) # 59
How Properties Work Under the Hood
| Operation | What actually runs |
|---|---|
p.salary = 100 |
Calls the setter with 100 |
x = p.salary |
Calls the getter, returns the result |
The user writes clean attribute-style code, but the getter/setter logic runs invisibly.
Key Takeaways & Recap
| Concept | Summary |
|---|---|
| Property | A special attribute backed by getter/setter logic |
| Private attribute | Prefixed with _; signals "don't touch from outside" |
@property |
Decorates the getter method |
@name.setter |
Decorates the setter method |
| Same name rule | Getter and setter must share the exact same method name |
| Why use it? | Enforce constraints (e.g. no negative salary, seconds 0–60) while keeping clean attribute syntax |