Introduction

Encapsulation is one of the fundamental aspects of Object Oriented Programming. It allows programmers better control of how data flows in their programs, and it protects that data. Encapsulation also makes objects into more self-sufficient and independently functioning pieces.

The concept of encapsulation builds on what you did in the last two guides with classes and constructors. Constructors usually are usually used in close conjunction with encapsulation and actually aid in making encapsulation work seamlessly.

Access Modifiers

Before you can take advantage of encapsulation, you have to understand how Python restricts access to the data stored in variables and methods.

Python has different levels of restriction that control how data can be accessed and from where. Variables and methods can be public, private, or protected. Those designations are made by the number of underscores before the variable or method.

Public

Every variable and method that you've seen so far with the exception of the constructors has been public. Public variables and methods can be freely modified and run from anywhere, either inside or outside of the class. To create a public variable or method, don't use any underscores.

Private

The private designation only allows a variable or method to be accessed from within its own class or object. You cannot modify the value of a private variable from outside of a class. Private variables and methods are preceded by two underscores. Take a look at the example below.
__make = 'Dodge'
Try using that class from before. Set the variables in the constructor to private. Then try to print one of the variables after an object has been instantiated.
class Car(object):

    def __init__(self, make = 'Ford', model = 'Pinto', year = '1971', mileage = '253812',      color = 'orange'):
        self.__make = make
        self.__model = model
        self.__year = year
        self.__mileage = mileage

        self.__color = color


    def move_forward(self, speed):
        print("Your %s is moving forward at %s" % (self.__model, speed))


    def move_backward(self, speed):
        print("Moving backward at %s" % speed)


mycar = Car()

print(mycar.__model)

You will receive an error message stating that the variable doesn't exist. This is because that variable is private. Now try running the move_forward method.
mycar.move_forward
Everything works fine. That's because the variable is being accessed by a method within the class, not externally.

There is a catch here. Python doesn't exactly handle protected variables as well as other object oriented languages. Instead of actually protecting variables, it changes the name of them within the interpreter. This allows for different copies of the variable to be created and exist. Try changing one of the protected variables in your mycar object and printing it out.
mycar.__model = 'Mustang'
print(mycar.__model)
Now, it seems to work, but what you've printed out is a strange copy of the protected variable. Try using the move_forward method again.
mycar.move_forward
It printed out the original value of __model. The variables exist independently. You can further illustrate this by printing out the object as a dictionary. You will see two different variables.
print(mycar.__dict__)

Protected

Protected variables and methods are very similar to private ones. You probably won't use protected variables or methods very often, but it's still worth knowing what they are. A variable that is protected can only be accessed by its own class and any classes derived from it. That is more a topic for later, but just be aware that if you are using a class as the basis of another one, protected variables may be a good option. Protected variables begin with a single underscore.

What Is Encapsulation

So, now that you know how access modifiers work, this next part is going to seem pretty obvious. Encapsulation is the process of using private variables within classes to prevent unintentional or potentially malicious modification of data. By containing and protecting variables within a class, it allows the class and the objects that it creates to function as independent, self contained, parts functioning within the machine of the program itself.

Through encapsulation variables and certain methods can only be interacted with through the interfaces designated by the class itself.

Setters and Getters

The interfaces that are used for interacting with encapsulated variables are generally referred to as setter and getter methods because the are used to set and retrieve the values of variables. Because methods exist within a class or object, they are able to access and modify private variables, while you will not be able to do so from outside the class. When you instantiated your mycar object, you essentially used its constructor as an initial setter method. Try writing a set of methods to set and get the value of one of the mycar variables.
def set_model(self, new_model):
	self.__model = new_model

def get_model(self):
	return self.__model
It might seem like a lot of extra work, but it's really not hard at all. Generally speaking, this is how you should structuring your classes and working with class variables.

Closing Thoughts

Encapsulation is a major part of Object Oriented Programming. It's a big part of what makes objects in programming perform more like physical objects in the real world. It also serves to protect the data stored within your objects and provides control and conventions for how you should handle the flow of data in and out of classes.

Exercises

  1. Make all of the variables in the Car class private.
  2. Try to print one from outside the class.
  3. Modify the move_forward method to use the private variable model, and call it.
  4. Create a setter method for one of the variables.
  5. Use the setter method that you created to change the value of a variable.
  6. Create a getter method for the same variable as the setter method.
  7. Use your getter method to access and print out that variable.