Public, Protected, and Private Naming Conventions

🏷️ Object-Oriented Programming (OOP) Basics / Encapsulation

🧭 Introduction

In Python, encapsulation is the practice of bundling data and methods together while controlling access to them. Unlike some other languages, Python does not have strict access modifiers like public, protected, or private. Instead, it uses naming conventions to signal how a variable or method should be used. These conventions help engineers write cleaner, more maintainable code by making the intended usage clear.


⚙️ What Are Naming Conventions?

Naming conventions are agreed-upon rules for how to name things in your code. In Python, they use underscores to indicate the level of access:

  • No underscore → Public
  • Single underscore → Protected
  • Double underscore → Private

These conventions are not enforced by the Python interpreter (except for name mangling with double underscores), but they act as a gentleman's agreement among engineers.


📊 Public Members

  • No underscore at the beginning of the name.
  • Accessible from anywhere — inside the class, outside the class, and from subclasses.
  • This is the default and most common type of member.

Example: A variable named name or a method named display_info is public.


🛡️ Protected Members

  • Single underscore at the beginning of the name (e.g., _salary).
  • Intended for internal use within the class and its subclasses.
  • Not meant to be accessed from outside the class, but Python does not prevent it.
  • Think of it as a "use at your own risk" signal.

Example: A variable named _balance or a method named _calculate_tax is protected.


🔒 Private Members

  • Double underscore at the beginning of the name (e.g., __password).
  • Triggers name mangling — Python internally renames the variable to _ClassName__variable.
  • This makes it harder (but not impossible) to access from outside the class.
  • Used for attributes that should not be accessed or overridden by subclasses.

Example: A variable named __api_key or a method named __encrypt_data is private.


🕵️ Comparison Table

Convention Syntax Example Accessibility Purpose
Public name Anywhere Default, open access
Protected _name Class and subclasses Internal use, not for external access
Private __name Only within the class Strongly hidden, prevents accidental override

🛠️ Practical Example

Consider a class called BankAccount:

  • Public attribute: account_holder — anyone can read or set this.
  • Protected attribute: _balance — intended for use by the class and its subclasses, but not by external code.
  • Private attribute: __pin — should only be used inside the class itself.

When you try to access __pin from outside, Python will raise an AttributeError unless you use the mangled name _BankAccount__pin.


🧠 Why Use These Conventions?

  • Clarity — Other engineers immediately understand how to use your code.
  • Maintainability — Reduces accidental misuse of internal details.
  • Encapsulation — Keeps the internal workings of a class hidden from the outside world.
  • Future-proofing — You can change internal implementation without breaking external code.

⚠️ Important Note

Python trusts the engineer. These conventions are not enforced by the language. A determined engineer can still access protected and private members. The goal is to communicate intent and encourage good habits, not to lock down access completely.


✅ Summary

  • Public — No underscore, open for all.
  • Protected — Single underscore, internal use.
  • Private — Double underscore, strongly hidden.

By following these simple naming conventions, you make your Python code more readable, predictable, and professional.


This topic shows how Python uses naming conventions to indicate whether an attribute or method is meant to be used freely, used only inside a class, or kept strictly internal.


🔧 Example 1: Public attribute — accessible anywhere

A public attribute has no underscore prefix and can be read or changed from outside the class.

class Car:
    def __init__(self, model):
        self.model = model

my_car = Car("Sedan")
print(my_car.model)

📤 Output: Sedan


🔧 Example 2: Protected attribute — single underscore prefix

A protected attribute starts with one underscore, signaling engineers that it should not be accessed directly outside the class.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self._salary = salary

    def show_salary(self):
        return self._salary

emp = Employee("Alice", 60000)
print(emp.show_salary())

📤 Output: 60000


🔧 Example 3: Private attribute — double underscore prefix

A private attribute starts with two underscores, causing Python to name-mangle it so it is harder to access from outside the class.

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance

    def get_balance(self):
        return self.__balance

account = BankAccount("Bob", 1000)
print(account.get_balance())

📤 Output: 1000


🔧 Example 4: Private method — double underscore prefix

A private method is intended for internal use only and is name-mangled to prevent accidental calls from outside.

class Logger:
    def __init__(self, name):
        self.name = name

    def __format_message(self, msg):
        return f"[{self.name}] {msg}"

    def log(self, msg):
        print(self.__format_message(msg))

logger = Logger("System")
logger.log("Starting up")

📤 Output: [System] Starting up


🔧 Example 5: Protected method in a subclass

A protected method can be overridden in a subclass, but engineers know it is not part of the public interface.

class Vehicle:
    def _start_engine(self):
        return "Engine started"

    def drive(self):
        return self._start_engine() + " — moving forward"

class ElectricCar(Vehicle):
    def _start_engine(self):
        return "Motor powered on"

my_car = ElectricCar()
print(my_car.drive())

📤 Output: Motor powered on — moving forward


Comparison Table

Convention Prefix Access from outside class Intended use
Public None Yes Any engineer
Protected _ Yes (discouraged) Subclass engineers
Private __ No (name-mangled) Internal only

🧭 Introduction

In Python, encapsulation is the practice of bundling data and methods together while controlling access to them. Unlike some other languages, Python does not have strict access modifiers like public, protected, or private. Instead, it uses naming conventions to signal how a variable or method should be used. These conventions help engineers write cleaner, more maintainable code by making the intended usage clear.


⚙️ What Are Naming Conventions?

Naming conventions are agreed-upon rules for how to name things in your code. In Python, they use underscores to indicate the level of access:

  • No underscore → Public
  • Single underscore → Protected
  • Double underscore → Private

These conventions are not enforced by the Python interpreter (except for name mangling with double underscores), but they act as a gentleman's agreement among engineers.


📊 Public Members

  • No underscore at the beginning of the name.
  • Accessible from anywhere — inside the class, outside the class, and from subclasses.
  • This is the default and most common type of member.

Example: A variable named name or a method named display_info is public.


🛡️ Protected Members

  • Single underscore at the beginning of the name (e.g., _salary).
  • Intended for internal use within the class and its subclasses.
  • Not meant to be accessed from outside the class, but Python does not prevent it.
  • Think of it as a "use at your own risk" signal.

Example: A variable named _balance or a method named _calculate_tax is protected.


🔒 Private Members

  • Double underscore at the beginning of the name (e.g., __password).
  • Triggers name mangling — Python internally renames the variable to _ClassName__variable.
  • This makes it harder (but not impossible) to access from outside the class.
  • Used for attributes that should not be accessed or overridden by subclasses.

Example: A variable named __api_key or a method named __encrypt_data is private.


🕵️ Comparison Table

Convention Syntax Example Accessibility Purpose
Public name Anywhere Default, open access
Protected _name Class and subclasses Internal use, not for external access
Private __name Only within the class Strongly hidden, prevents accidental override

🛠️ Practical Example

Consider a class called BankAccount:

  • Public attribute: account_holder — anyone can read or set this.
  • Protected attribute: _balance — intended for use by the class and its subclasses, but not by external code.
  • Private attribute: __pin — should only be used inside the class itself.

When you try to access __pin from outside, Python will raise an AttributeError unless you use the mangled name _BankAccount__pin.


🧠 Why Use These Conventions?

  • Clarity — Other engineers immediately understand how to use your code.
  • Maintainability — Reduces accidental misuse of internal details.
  • Encapsulation — Keeps the internal workings of a class hidden from the outside world.
  • Future-proofing — You can change internal implementation without breaking external code.

⚠️ Important Note

Python trusts the engineer. These conventions are not enforced by the language. A determined engineer can still access protected and private members. The goal is to communicate intent and encourage good habits, not to lock down access completely.


✅ Summary

  • Public — No underscore, open for all.
  • Protected — Single underscore, internal use.
  • Private — Double underscore, strongly hidden.

By following these simple naming conventions, you make your Python code more readable, predictable, and professional.

Interactive Views

You are currently in 📚 All-in-One mode. Use the tabs at the top to switch to 📖 Theory Only or 💻 Code Only views.

This topic shows how Python uses naming conventions to indicate whether an attribute or method is meant to be used freely, used only inside a class, or kept strictly internal.


🔧 Example 1: Public attribute — accessible anywhere

A public attribute has no underscore prefix and can be read or changed from outside the class.

class Car:
    def __init__(self, model):
        self.model = model

my_car = Car("Sedan")
print(my_car.model)

📤 Output: Sedan


🔧 Example 2: Protected attribute — single underscore prefix

A protected attribute starts with one underscore, signaling engineers that it should not be accessed directly outside the class.

class Employee:
    def __init__(self, name, salary):
        self.name = name
        self._salary = salary

    def show_salary(self):
        return self._salary

emp = Employee("Alice", 60000)
print(emp.show_salary())

📤 Output: 60000


🔧 Example 3: Private attribute — double underscore prefix

A private attribute starts with two underscores, causing Python to name-mangle it so it is harder to access from outside the class.

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.__balance = balance

    def get_balance(self):
        return self.__balance

account = BankAccount("Bob", 1000)
print(account.get_balance())

📤 Output: 1000


🔧 Example 4: Private method — double underscore prefix

A private method is intended for internal use only and is name-mangled to prevent accidental calls from outside.

class Logger:
    def __init__(self, name):
        self.name = name

    def __format_message(self, msg):
        return f"[{self.name}] {msg}"

    def log(self, msg):
        print(self.__format_message(msg))

logger = Logger("System")
logger.log("Starting up")

📤 Output: [System] Starting up


🔧 Example 5: Protected method in a subclass

A protected method can be overridden in a subclass, but engineers know it is not part of the public interface.

class Vehicle:
    def _start_engine(self):
        return "Engine started"

    def drive(self):
        return self._start_engine() + " — moving forward"

class ElectricCar(Vehicle):
    def _start_engine(self):
        return "Motor powered on"

my_car = ElectricCar()
print(my_car.drive())

📤 Output: Motor powered on — moving forward


Comparison Table

Convention Prefix Access from outside class Intended use
Public None Yes Any engineer
Protected _ Yes (discouraged) Subclass engineers
Private __ No (name-mangled) Internal only