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.
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:
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
Hide Implementation
Encapsulation hides how data is stored and manipulated internally. Users interact through methods, not direct variable access.
Control Access
By using methods to access data, you can add validation, logging, or other logic. This prevents invalid data from being set!
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:
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":
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
Single Underscore
Prefix an attribute with _ to indicate it's protected. This is a convention, not enforced by Python!
Still Accessible
Protected attributes can still be accessed directly, but the convention tells other programmers "this is internal, use methods instead!"
Use Methods
It's best practice to access protected attributes through methods (getters/setters) rather than directly.
Mini Practice #1: Protected Attributes
Try It YourselfCreate a class with protected attributes:
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:
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
Double Underscore
Prefix an attribute with __ to make it private. Python performs "name mangling" - it changes the name internally!
Name Mangling
Python changes __balance to _BankAccount__balance internally. This makes it harder to access accidentally!
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 YourselfCreate a class with private attributes:
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:
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.
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.