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).
Top comments (0)