Private and Public Attributes in Python

โ– Introduction

If you are familiar with any other programming languages like Java or C++ you must have come across the terms private attributes and public attributes. They are usually also known as the Access modifiers.  In languages like C++ and java, there are keywords used to restrict and make the member public or private.

โœ๏ธ Access Modifiers are used to restrict or control the accessibility of class resources by declaring them as public, private, and protected

But, there are no keywords known as public, private, and protected in Python. Hence, we have to use certain conventions to restrict the members.

Without further ado, let’s just dive in to learn more about the modifiers.

๐Ÿ“š Public Attribute In Python

  • In Python, every member of the class is public by default.
  • Public members in a class can be accessed from anywhere outside the class.
  • You can access the public members by creating the object of the class.

Example 1:

class Music:
    # Creating a constructor
    def __init__(self):
        self.genre = "Pop"
        self.singer = "Coldplay"
        # These are public attributes

    # Creating a function
    def foo(self):
        song = 'Hymn For The Weekend'
        return song


# Creating object of the class
m = Music()

# Accessing the members inside the class
print("Song: ", m.foo())
print("Genre:", m.genre)
print("Singer:", m.singer)

Output:

Song:  Hymn For The Weekend
Genre: Pop
Singer: Coldplay

๐Ÿ“Note: Even derived class can access the public members.

Example 2:

๐Ÿ“š Private Attributes in Python

Unfortunately, Python does not have a way to effectively restrict access to instance variables or methods. ๐Ÿ˜Ÿ

However, we do have a workaround. To declare a member as private in Python, you have to use double underscore __ as a prefix to the variables. Private members are restricted within the same class, i.e. we can access the private members only within the same class.

Example:

class Music:
    # constructor
    def __init__(self):
        # These are private variables
        self.__genre = "Pop"
        self.__singer = "Coldplay"

    # private function
    def __func(self):
        print('Music: Hym For The Weekend')

    def foo(self):
        # Accessing private members of the class
        obj.__func()
        print("Genre:", obj.__genre)
        print("Singer:", obj.__singer)


obj = Music()  # Creating an object of the Music class
obj.foo()  # calling the private function

Output:

Music: Hym For The Weekend
Genre: Pop
Singer: Coldplay

Explanation:

In the above example, we have used two underscores before the identifiers (self.__genre and self.__singer) to make the variables private. Similarly, the same convention was used for the function func which ensured that it is a private method.

โ˜ ๏ธ CAUTION

If you try to access the private member outside the class, you will get an AttributeError.

Example:

class Music:
    # constructor
    def __init__(self):
        # These are private variables
        self.__genre = "Pop"
        self.__singer = "Coldplay"

    # private function
    def __func(self):
        print('Music: Hym For The Weekend')

    def foo(self):
        # Accessing private members of the class
        print("Genre:", obj.__genre)
        print("Singer:", obj.__singer)


# Creating object of the class
obj = Music()


# Trying to access the private attributes from outside the class
obj.__func()
print("Genre:", obj.__genre)
print("Singer:", obj.__singer)

Output:

Traceback (most recent call last):
  File "main.py", line 24, in <module>
    obj.__func()
AttributeError: 'Music' object has no attribute '__func'

So, this brings us to the question – How to access the private attributes from outside the class? Is there a way?๐Ÿค”

When you use a double underscore (e.g.,ย __var), Python plays around with the name giving it properties of a private attribute. However, the variable can still be accessed from outside the class using its obfuscated name. Hence, it is not strictly private.

This brings us to a very important concept in Python – Name Mangling. You can access the private attributes outside the class using name mangling.

โœจ Name Mangling in Python

Name Mangling is a process in Python, where, if a method has, in any event, two underscores before the name, and at the most one underscore following the name, it gets replaced with _ClassName before it, for instance, __method() becomes _ClassName__ method(). Since the name is changed internally by the interpreter, so we cannot access the variable utilizing its original name and that is how you can hide data in Python.

Note: Name Mangling is essentially used to avoid overriding the methods for parent classes by inherited classes. 

Let’s look at an example to understand how private variables are accessed outside the class with the help of name mangling:

# Defining a class
class Music:
    # Creating a constructor
    def __init__(self):
        # These are private attributes
        self.__genre = "Pop"
        self.__singer = "Coldplay"
        # This is a public attribute
        self.releaseyear = 2000

    # Creating a function
    def foo(self):
        print("Song: Trouble")


# Creating object of the class
obj = Music()
# Calling the method inside the class
obj.foo()

# Accessing the private members outside the class using name mangling
print("Genre:", obj._Music__genre)
print("Singer:", obj._Music__singer)

# Accessing the public member normally
print("Year of release:", obj.releaseyear)

Output:

Song: Trouble
Genre: Pop
Singer: Coldplay
Year of release: 2000

๐Ÿ“š Protected Attributes in Python

You can access protected attributes of a class from within the class, and they can also be accessed by the sub-classes. This facilitates inheritance in Python.

To make a variable protected, you have to add a single underscore (e.g. _x) as a prefix to it. To make it truly protected, you also have to use a property decorator.

Example:

# Defining a class
class Music:
    def __init__(self):
        self._song = 'Trouble'

    @property
    def foo(self):
        return self._song

    @foo.setter
    def foo(self, new_song):
        # overloading foo
        self._song = new_song


obj = Music()
print('Song - ', obj.foo)
obj.foo = 'Hym For The Weekend'
print('New Song - ', obj.foo)

Output:

Song -  Trouble
New Song -  Hym For The Weekend

Explanation:

  • @property decorator ensured that foo() method is a property.
  • @foo.setter decorator allowed us to overload the foo() method. Now, the variable song is protected. But this

๐Ÿšจ Alert! – You can still access song from outside using obj._song. Thus you should always avoid accessing or modifying variables prefixed by _ from outside the class.

โ– Conclusion

Thus, we learned about one of the most important OOP concepts in Python in this tutorial, i.e., how you can use public, private and protected attributes in Python.

I hope this article helped you. Please stay tuned and subscribe for more interesting discussions in the future.

Authors:
๐Ÿ‘จโ€๐ŸŽ“ SHUBHAM SAYON
๐Ÿ‘ฉโ€๐ŸŽ“ RASHI AGARWAL