Why Python does not have true private variables
Tags: python, programming
Published: 2022-11-16 17:44:11

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:

```python
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 "__") ...

```python
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: 
```bash
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*:

```python
foo = Foo()
print(dir(foo))
```
This has output:
```bash
['_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:
```python

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:
```bash
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).