Unit 6 • Lesson 6

Encapsulation and Access Control

Overview

Encapsulation keeps object data private and secure by restricting direct access. You'll learn to use naming conventions like _ and __ to hide variables and protect internal functionality from unwanted changes, following best practices for data protection.

Intermediate 20–25 min

What You Will Learn in This Lesson

By the end of this lesson, you will know:

  • Encapsulation: Understand what encapsulation is and why it's important.
  • Public attributes: Learn about public attributes that can be accessed directly.
  • Protected attributes: Discover the _ convention for protected attributes.
  • Private attributes: Learn the __ convention for private attributes.

Why This Matters

Encapsulation protects your object's internal data from being accidentally changed or accessed incorrectly. It's like having a lock on your diary - you control who can read or write to it. By using naming conventions, you signal to other programmers (and yourself) which parts of your class are meant to be public, protected, or private. This prevents bugs and makes your code more maintainable!

Step 1: What is Encapsulation?

Encapsulation is the practice of hiding internal details and controlling access to an object's data:

Encapsulation Concept
class BankAccount:
    def __init__(self, balance):
        self.balance = balance  # Public - anyone can access
    
    def deposit(self, amount):
        if amount > 0:
            self.balance += amount  # Controlled access through method
1

Hide Implementation

Encapsulation hides how data is stored and manipulated internally. Users interact through methods, not direct variable access.

2

Control Access

By using methods to access data, you can add validation, logging, or other logic. This prevents invalid data from being set!

3

Protect Data

Encapsulation protects your object's internal state from being accidentally corrupted or accessed incorrectly.

Key Concept: Encapsulation is like a vending machine - you don't need to know how it works internally. You just press buttons (methods) and get what you want. The internal mechanisms (private data) are protected!

Step 2: Public Attributes

Public attributes can be accessed directly from outside the class:

Public Attributes
class Person:
    def __init__(self, name):
        self.name = name  # Public attribute

person = Person("Alice")
print(person.name)  # Direct access - OK!
person.name = "Bob"  # Direct modification - OK!

Public by Default

In Python, all attributes are public by default - you can access and modify them directly. This is simple but can be dangerous if you want to protect your data!

Step 3: Protected Attributes (_)

Protected attributes use a single underscore prefix. This is a convention that signals "internal use":

Protected Attributes
class BankAccount:
    def __init__(self, balance):
        self._balance = balance  # Protected (convention)
    
    def get_balance(self):
        return self._balance

account = BankAccount(100)
print(account._balance)  # Works, but convention says "don't do this"
print(account.get_balance())  # Better - use the method
1

Single Underscore

Prefix an attribute with _ to indicate it's protected. This is a convention, not enforced by Python!

2

Still Accessible

Protected attributes can still be accessed directly, but the convention tells other programmers "this is internal, use methods instead!"

3

Use Methods

It's best practice to access protected attributes through methods (getters/setters) rather than directly.

Mini Practice #1: Protected Attributes

Try It Yourself

Create a class with protected attributes:

Press Run to see output

What happened? The _grade attribute is protected (single underscore). We access it through methods (get_grade() and set_grade()), which allows us to add validation. The set_grade() method ensures grades are between 0 and 100!

Step 4: Private Attributes (__)

Private attributes use double underscores. Python actually changes their name internally:

Private Attributes
class BankAccount:
    def __init__(self, balance):
        self.__balance = balance  # Private (name mangling)
    
    def get_balance(self):
        return self.__balance

account = BankAccount(100)
print(account.get_balance())  # Works!
# print(account.__balance)  # Error! AttributeError
1

Double Underscore

Prefix an attribute with __ to make it private. Python performs "name mangling" - it changes the name internally!

2

Name Mangling

Python changes __balance to _BankAccount__balance internally. This makes it harder to access accidentally!

3

Use Methods

Private attributes should only be accessed through methods. This gives you full control over how data is accessed and modified!

Name Mangling

When you use __, Python automatically changes the name to _ClassName__attribute. This prevents accidental access and name conflicts in inheritance. It's not true privacy (you can still access it if you know the mangled name), but it's a strong signal!

Mini Practice #2: Private Attributes

Try It Yourself

Create a class with private attributes:

Press Run to see output

What happened? The __secret attribute is private (double underscore). We can only access it through methods like reveal() and change_secret(). This protects the data and gives us control over how it's accessed!

Step 5: Getters and Setters

Getters and setters are methods that control access to attributes:

Getters and Setters
class Temperature:
    def __init__(self, celsius):
        self.__celsius = celsius
    
    def get_celsius(self):  # Getter
        return self.__celsius
    
    def set_celsius(self, celsius):  # Setter
        if celsius < -273.15:
            raise ValueError("Temperature too low!")
        self.__celsius = celsius

temp = Temperature(25)
print(temp.get_celsius())  # 25
temp.set_celsius(30)  # Valid
# temp.set_celsius(-300)  # Raises ValueError!

Remember: Getters and setters let you add validation, logging, or computation when accessing attributes. They're the proper way to access protected or private attributes!

End-of-Lesson Exercises

Exercise 1: Protected Attributes

Create a BankAccount class with a protected attribute _balance (set to 0 in __init__). Add methods get_balance() and deposit(amount). Create an account, deposit 100, and print the balance.

Define class BankAccount, add _balance protected attribute, add get_balance and deposit methods, create object, deposit, print balance.

Write your code above and click "Check Answer" to verify it's correct.

Exercise 2: Private Attributes

Create a Person class with a private attribute __age. Add methods get_age() and set_age(age) that validates age is between 0 and 150. Create a Person, set age to 25, and print it.

Define class Person, add __age private attribute, add get_age and set_age with validation, create object, set age, print age.

Write your code above and click "Check Answer" to verify it's correct.