CodeMIRAI
0 XP

Properties and encapsulation

2 min read 5 runnable examples Quiz included

Encapsulation means an object looks after its own data: other code uses its methods instead of reaching in and changing values directly. That way the object can keep its data valid.

The problem

Python
class Account:
    def __init__(self, balance):
        self.balance = balance

acct = Account(100)
acct.balance = -5000      # nothing stops this
print(acct.balance)

Private by convention

Python has no truly private attributes. Instead, a leading underscore says "internal, don't touch from outside":

Python
class Account:
    def __init__(self, balance):
        self._balance = balance      # internal by convention

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("Deposit must be positive")
        self._balance += amount

    def get_balance(self):
        return self._balance

acct = Account(100)
acct.deposit(50)
print(acct.get_balance())

Programmers respect the underscore. (Two underscores, __balance, trigger "name mangling", which makes accidental access harder; it's rarely needed.)

@property: attributes with logic

get_balance() works, but acct.balance reads more naturally. A property looks like an attribute but runs a method:

Python
class Account:
    def __init__(self, balance):
        self._balance = balance

    @property
    def balance(self):              # runs on acct.balance
        return self._balance

    @balance.setter
    def balance(self, value):       # runs on acct.balance = ...
        if value < 0:
            raise ValueError("Balance can't be negative")
        self._balance = value

acct = Account(100)
print(acct.balance)
acct.balance = 250
print(acct.balance)
acct.balance = -5

Code using the class still writes acct.balance, but every change goes through the validation.

Computed properties

A property can calculate its value every time it's read, so it's never out of date:

Python
class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @property
    def area(self):
        return self.width * self.height

r = Rectangle(3, 4)
print(r.area)
r.width = 10
print(r.area)       # updated automatically

Without a setter, a property is read-only:

Python
class Circle:
    def __init__(self, radius):
        self.radius = radius

    @property
    def diameter(self):
        return self.radius * 2

c = Circle(5)
print(c.diameter)
c.diameter = 20
Finished this lesson?Mark it complete to earn +20 XP and move your progress on.