Why Python does not have true private variables
jzfrank
Posted on November 16, 2022
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")
... 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)
The output gives something like this:
public print message
not secret
'Foo' object has no attribute '__private_print'
'Foo' object has no attribute '__secret'
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))
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']
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)
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
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).
Posted on November 16, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.