python-encapsulation.md

#Python Encapsulation

Encapsulation is about protecting data inside a class. It means keeping data (properties) and methods together in a class while controlling how that data is accessed from the outside.

This prevents accidental changes to your data and hides the internal details of how your class works.


#Private Properties

In Python, you can make properties private by using a double underscore __ prefix.

#Example: Create a private class property

pythonclass Person:
  def __init__(self, name, age):
    self.name = name
    self.__age = age # Private property

p1 = Person("Emil", 25)
print(p1.name)
# print(p1.__age) # This will cause an error
Note: Private properties cannot be accessed directly from outside the class.

#Get Private Property Value

To access a private property, you can create a getter method.

#Example: Use a getter method

pythonclass Person:
  def __init__(self, name, age):
    self.name = name
    self.__age = age

  def get_age(self):
    return self.__age

p1 = Person("Tobias", 25)
print(p1.get_age())

#Set Private Property Value

To modify a private property, you can create a setter method. This allows you to validate the value before applying it.

#Example: Use a setter method

pythonclass Person:
  def __init__(self, name, age):
    self.name = name
    self.__age = age

  def get_age(self):
    return self.__age

  def set_age(self, age):
    if age > 0:
      self.__age = age
    else:
      print("Age must be positive")

p1 = Person("Tobias", 25)
p1.set_age(26)
print(p1.get_age())

#Why Use Encapsulation?

Encapsulation provides several key benefits:

#Example: Protect and validate data

pythonclass Student:
  def __init__(self, name):
    self.name = name
    self.__grade = 0

  def set_grade(self, grade):
    if 0 <= grade <= 100:
      self.__grade = grade
    else:
      print("Grade must be between 0 and 100")

  def get_grade(self):
    return self.__grade

  def get_status(self):
    if self.__grade >= 60:
      return "Passed"
    else:
      return "Failed"

student = Student("Emil")
student.set_grade(85)
print(student.get_grade())
print(student.get_status())

#Protected Properties

Python also has a convention for protected properties using a single underscore _ prefix.

#Example: Create a protected property

pythonclass Person:
  def __init__(self, name, salary):
    self.name = name
    self._salary = salary # Protected property

p1 = Person("Linus", 50000)
print(p1.name)
print(p1._salary) # Can access, but shouldn't
Note: A single underscore _ is just a convention. It tells other programmers that the property is intended for internal use, but Python doesn't enforce this restriction.

#Private Methods

You can also make methods private using the double underscore prefix. These can only be called by other methods inside the class.

#Example: Create a private method

pythonclass Calculator:
  def __init__(self):
    self.result = 0

  def __validate(self, num):
    if not isinstance(num, (int, float)):
      return False
    return True

  def add(self, num):
    if self.__validate(num):
      self.result += num
    else:
      print("Invalid number")

calc = Calculator()
calc.add(10)
print(calc.result)
# calc.__validate(5) # This would cause an error

#Name Mangling

Name mangling is how Python implements private properties and methods. When you use , Python automatically renames it internally by adding ClassName in front (e.g., age becomes Person__age).

#Example: See how Python mangles the name

pythonclass Person:
  def __init__(self, name, age):
    self.name = name
    self.__age = age

p1 = Person("Emil", 30)

# This is how Python mangles the name:
print(p1._Person__age) # Not recommended!
Note: While you can access private properties using the mangled name, it is not recommended as it defeats the purpose of encapsulation.