Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing private class variable

While learning Python, I came accross this strange behavior :

Consider this simple class :

class Human(object):
  def __init__(self, name):
    self.__name = name

  def description(self):
    print("Hello, my name is %s" % self.__name)

I want to avoid being able to alter the name after the object has been created.

If I use this :

MyHuman = Human("Andy")
MyHuman.__name = "Arthur"
MyHuman.age = 23

MyHuman.description()
print MyHuman.age

It doesn't change the name after the object instanciation, which is good because I want the instance variable to be private. On the other hand, I thought it would complain about accessing this variable. It doesn't even complain about accessing a mysterious "age" variable and prints it properly later.

I come from C# and it seems odd to me. Where is my mistake ?

like image 666
Andy M Avatar asked Dec 25 '22 17:12

Andy M


2 Answers

You should know the following:

  1. When you write to an attribute of an object, that does not exist, the python system will normally not complain, but just create a new attribute.
  2. Private attributes are not protected by the Python system. That is by design decision.
  3. Private attributes will be masked. The reason is, that there should be no clashes in the inheritance chain. The masking is done by some implicit renaming. Private attributes will have the real name
  "_<className>__<attributeName>"

With that name, it can be accessed from outside. When accessed from inside the class, the name will be automatically changed correctly.

like image 153
Juergen Avatar answered Dec 28 '22 06:12

Juergen


To use emulate readonly/private variables in Python, use property syntax. (Comments/other answers point out the variable can still be accessed if you know how--thanks for the heads up). Here is one way to do it:

>>> class Test:
        def __init__(self):
            self.__a = 1

        @property
        def a(self):
            return self.__a

>>> t = Test()
>>> t.a
1
>>> t.a = 2
AttributeError: cannot set attribute
>>> t.__a
AttributeError: 'Test' object has no attribute '__a'
like image 29
Sam Avatar answered Dec 28 '22 07:12

Sam