DEV Community

Cover image for Why Python does not have true private variables
jzfrank
jzfrank

Posted on • Edited on

Why Python does not have true private variables

You may have heard this claim:

Different from Java or C#, Python has no true private variables.

But wait... doesn't Python have the double underscore mechanism? If we define variables of a class starting with "__", wouldn't that make the variables inaccessible?

Consider the following code:

class Foo:
    def __init__(self):
        self.__secret = "secret"
        self.not_secret = "not secret"

    def __private_print(self):
        print("private print message")

    def pubic_print(self):
        print("public print message")
Enter fullscreen mode Exit fullscreen mode

... and let's try call the "private" methods/variables (that starts with "__") ...

if __name__ == "__main__":
    foo = Foo()
    # of course, the fields not starting with __ should be accessible
    foo.pubic_print()
    print(foo.not_secret)
    # now let's try these "private" fields
    try:
        foo.__private_print()
    except Exception as e:
        print(e)
    try:
        print(foo.__secret)
    except Exception as e:
        print(e)

Enter fullscreen mode Exit fullscreen mode

The output gives something like this:

public print message
not secret
'Foo' object has no attribute '__private_print'
'Foo' object has no attribute '__secret'
Enter fullscreen mode Exit fullscreen mode

You yelled out: See? They are not accessible, so Python does have private fields!

Or, DOES IT?

The trick is, although we cannot define a field ("__secret"), then try access it by using ("obj.__secret"), we can still have clever workarounds to access the field.

How so?

The secret (is it a pun? maybe...) lies in: Python will automatically generate accessible fields for those fields starting with underscores.

To witness, we can use dir on object foo:

foo = Foo()
print(dir(foo))
Enter fullscreen mode Exit fullscreen mode

This has output:

['_Foo__private_print', '_Foo__secret', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'not_secret', 'pubic_print']
Enter fullscreen mode Exit fullscreen mode

Do you notice "_Foo__private_print" and "_Foo__secret"? Looks familiar? Yes, they are the accessible fields automatically generated by Python.

Now the code:


class Foo:
    def __init__(self):
        self.__secret = "secret"
        self.not_secret = "not secret"

    def __private_print(self):
        print("private print message")

    def pubic_print(self):
        print("public print message")


if __name__ == "__main__":
    foo = Foo()
    # of course, the fields not starting with __ should be accessible
    foo.pubic_print()
    print(foo.not_secret)
    # now let's try these "private" fields
    try:
        foo.__private_print()
    except Exception as e:
        print(e)
    try:
        print(foo.__secret)
    except Exception as e:
        print(e)
    print(dir(foo))
    foo._Foo__private_print()
    print(foo._Foo__secret)

Enter fullscreen mode Exit fullscreen mode

gives:

public print message
not secret
'Foo' object has no attribute '__private_print'
'Foo' object has no attribute '__secret'
['_Foo__private_print', '_Foo__secret', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'not_secret', 'pubic_print']
private print message
secret
Enter fullscreen mode Exit fullscreen mode

So, in conclusion, although we cannot directly access fields that defined with starting double underscore "__", Python automatically generated fields that could be accessible. Python advises programmers NOT to access fields starting with "__" (and of course we should follow the suggestion). But nevertheless, you may find a workaround to access it. That is why Python has no true private variables (or methods).

Top comments (0)