True private attributes in Python

Home

Some background - Private attributes in a class

In many programming languages there is support for making variables private to prevent unintended consequences. But in Python there is no direct support for it in class definitions. All attributes of an object in Python are available publicly. For better understanding lets take an example.

import pprint
class SomeClass:
    def __init__(self, a):
          self.a = a  # Meant for public use
          self._b = "Private but is directly available for public use"
          self.__c = "Private but name is changed for public use"
    def __test(self):
          return self._b
pprint.pprint(dir(SomeClass(2)))

Which produces the following output:

['_SomeClass__c',
 '_SomeClass__test',
 ...,
 '_b',
 'a']

These are all the attributes that we can use on an object of SomeClass. So, all of the following will work:

obj = SomeClass()
obj._SomeClass__c
obj._SomeClass__test()
obj._b
obj.a

What we realize here is that the value of obj._SomeClass__c is the same as the value of private variable __c. What we instead want is deny public access to __c but that isn’t straight forward. A well motivated user of the class would be able to use the private attributes without much issue.

This is where local variables and local functions come into play.

The solution - Using local variables in local functions

Functions are a great way to make the code more modular and we can even treat them as objects. Lets see how we define a class and a function with private variables and methods.

from typing import Callable
from dataclasses import dataclass

@dataclass(slots=True) # Restricts creating new attributes
class SomeClass:
    x: int
    y: int
    func: Callable[[], int]

def create_SomeClass(x, y):
    k = x / y  # Private variable
    def private_func(z):  # Can't be accessed outside create_SomeClass
          return k - z

    def public_func():
          return x + private_func(y * 2)

    return SomeClass(x=x, y=y, func=public_func)

obj = create_SomeClass(2, 5)
print(obj.func())
-7.6