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)