Curriculum
In Python, private attributes are attributes that are intended to be used only within the class they are defined in, and are not intended to be accessed or modified directly from outside the class. Private attributes are usually denoted with a leading underscore (_) to indicate that they should not be accessed from outside the class.
Here is an example to demonstrate the use of private attributes in Python:
class Person:
    def __init__(self, name, age):
        self._name = name
        self._age = age
    def display(self):
        print("Name: ", self._name)
        print("Age: ", self._age)
    def _get_name(self):
        return self._name
    def _set_name(self, name):
        self._name = name
    name = property(_get_name, _set_name)
p = Person("John", 30)
p.display()
# Accessing private attribute outside the class
print("Name: ", p._name)  # This will print "John"
# Using the property to access and modify the private attribute
print("Name using property: ", p.name)  # This will print "John"
p.name = "Bob"
print("Name using property after modification: ", p.name)  # This will print "Bob"
In the example above, we have a class Person with two private attributes _name and _age. These attributes are defined with leading underscores to indicate that they are private and should not be accessed from outside the class.
We have also defined getter and setter methods _get_name and _set_name for the _name attribute. We have then used the property function to create a name property which can be used to access and modify the _name attribute.
We can see that we are able to access the private attribute _name outside the class using p._name, but this is not recommended as it violates the principle of encapsulation.
Instead, we can use the name property to access and modify the _name attribute in a more controlled and safe way. The name property provides a way to read and write the _name attribute, while still enforcing any logic or constraints that we may have defined in the getter and setter methods.
Name mangling is a feature in Python that allows a class to “hide” its attributes from outside the class, even if they are not marked as private attributes with a leading underscore. Name mangling is achieved by prefixing the attribute name with two underscores (__) but not ending with underscores (e.g. __name).
When an attribute is name-mangled, its name is modified by prefixing it with an underscore and the class name, like _ClassName__attribute_name. This makes it more difficult for outside code to access or modify the attribute directly, since the mangled name is unique to the class and not easily guessed.
Here is an example to demonstrate the use of name mangling in Python:
class Person:
    def __init__(self, name, age):
        self.__name = name
        self.__age = age
    def display(self):
        print("Name: ", self.__name)
        print("Age: ", self.__age)
p = Person("John", 30)
p.display()
# Accessing private attribute directly from outside the class
print("Name: ", p.__name)  # This will raise an AttributeError
# Accessing private attribute with name mangling
print("Name with name mangling: ", p._Person__name)  # This will print "John"
In the example above, we have a class Person with two private attributes __name and __age. These attributes are name-mangled to _Person__name and _Person__age, respectively.
We can see that we are not able to access the private attribute __name directly from outside the class, as attempting to do so will raise an AttributeError. However, we can still access the attribute using name mangling, by prefixing the attribute name with the class name.
It’s worth noting that name mangling is not intended to be a security feature, but rather a way to avoid naming conflicts in subclasses. It’s still possible for outside code to access name-mangled attributes, albeit with a bit more effort.