Legends ofPythos
Claim your name
Objects and Classes

Keeping internals private-ish

Lesson 11 of 13

Watch the lesson1:05 · with Torsten
In many languages, you can mark fields as private so other code cannot touch them. Python takes a different approach: it trusts that developers will behave.

The single underscore convention

class BankAccount:
    def __init__(self, owner):
        self.owner = owner
        self._balance = 0.0

acc = BankAccount('Ada')
print(acc._balance) # prints 0.0
_balance signals 'internal use only'
Prefixing an attribute with a single underscore (like _balance) is a convention. It tells other programmers: "This is for the class's internal machinery. Don't rely on it or change it directly." Python does not actually prevent access, though.

Using @property to control reads

class Player:
    def __init__(self):
        self._health = 100

    @property
    def health(self):
        return self._health

p = Player()
print(p.health) # prints 100
# p.health = 50 would raise AttributeError
@property makes health look like an attribute but run code
The @property decorator lets you define a method that behaves like a read-only attribute. When someone writes p.health, Python calls your function and returns the result, instead of letting them poke at _health directly.

Adding validation with setters

class Player:
    def __init__(self):
        self._level = 1

    @property
    def level(self):
        return self._level

    @level.setter
    def level(self, value):
        if not isinstance(value, int) or value < 1:
            raise ValueError("Level must be a positive integer")
        self._level = value

p = Player()
p.level = 5   # works fine
# p.level = -2 would raise an error
@name.setter lets you validate before storing
By attaching a setter to the same property name, you can check incoming values. If they are invalid, you raise an exception and never store bad data in _level. This keeps your object's state consistent.

Your turn

0 of 2 solved

Exercise 1

+35 XP
Create a class called Thermometer. Its __init__ takes a temperature in Celsius and stores it in an internal attribute _celsius. Use the single underscore convention for this attribute. Add a read-only property named fahrenheit that calculates and returns the temperature in Fahrenheit using the formula: (C * 9/5) + 32.
Write your Python here.

Run your code to check it against the tests.

Exercise 2

+35 XP
Create an Inventory class. Store the value passed to __init__ in an internal attribute _count. Expose it through a count property, and add a setter that raises ValueError if the new value is negative.
class Inventory:
    def __init__(self, count):
        self._count = count

Run your code to check it against the tests.