-1

I encountered a strange issue while using python. consider the below code:

class Foo:
    __bar=None

foo=Foo()
foo.bar=10
print(foo.bar)

I beleive I should get an error here. But I get the output as 10. I get the same output even if I change the code as:

foo=Foo()
foo.__bar=10
print(foo.__bar)

can someone explain why I am not getting an error here? Also what should I do to get an error? Edited for additional clarification:

class Foo:
    def __init__(self):
        self.__bar=10
    def get_bar(self):
        return self.__bar

foo=Foo()
foo.__bar=20
print(foo.__bar)
print(foo.get_bar())

Can someone explain why I am getting the answer as 20 and 10? I thought I will get an error for trying to access a private instance variable outside the class. Thanks for the help.

codingsplash
  • 4,785
  • 12
  • 51
  • 90
  • Why do you believe that you should get an error? What are you doing wrong, in your opinion? – Hashmush Feb 18 '15 at 05:46
  • 1
    Underscores for "private" variables is merely a convention. Nothing more. Also, `bar` and `__bar` are not the same property. Nothing in Python says that this should trigger anything. – deceze Feb 18 '15 at 05:46
  • wth, this is such a duplicate. did someone un-close it? – nneonneo Feb 18 '15 at 06:03
  • This question is not a duplicate, becuase it is not asking about leading double-underscore in general, but about a specific usage, and none of the answers in the other question cover the answer to this question. – shx2 Feb 18 '15 at 06:03
  • The related question (but it's not a duplicate) that people were citing is [What is the meaning of single and double underscore before an object name?](https://stackoverflow.com/questions/1301346/what-is-the-meaning-of-single-and-double-underscore-before-an-object-name) – smci Jul 16 '22 at 18:02

2 Answers2

4

First of all, you should read the answers to the related quesion.

Second of all, don't confuse between class-members and instance members. Your definition of the class Foo includes a class member named __bar, but assigning to foo.__bar adds an instance-member which overshadows it (and not overwrites it). Those are different things.

Now, when you assign (or in general, access) a leading-double-underscore attribute from "outside" (in your example, using foo.__bar), the name does not get mangaled, and everything works without extra magic. However, the standard way of accessing leading-double-underscore attributes is from within methods of the class (using self.__bar), and that is when the magic happens.

To demonstrate:

class Foo(object): pass

f = Foo()
foo.__bar = 10
foo.__bar
=> 10
foo.__dict__.keys()
=> dict_keys(['__bar'])

class Foo(object):
    def __init__(self):
       self.__bar = 10

foo = Foo()
foo.__dict__.keys()
=> dict_keys(['_Foo__bar'])  # mangaled!
foo.__bar  # cannot access from "outside"
=> AttributeError
shx2
  • 61,779
  • 13
  • 130
  • 153
0

There is some basics that you need to know. In python, declaring a member variable as private by using __ before its name does not mean its access is limited outside. Python does not impose such restrictions but what it does internally is name mangling where the private member variable's name is mangled as _clasname__variablename. You can see this by logging print(dir(objectname)) In your case, it will be _Foo__bar and you can use it to read and update the private variable. Below is your code snippet with explanation.

class Foo:
  def __init__(self):
      self.__bar=10 # private varaiable is created and assigned a value of 10
  def get_bar(self):
      return self.__bar # returns the private variable as you are accesing it within the class

foo=Foo()
print(dir(foo)) # prints the private variable with its name mangled
foo.__bar=20 # instance member variable is created and assigned a value of 20
print(foo.__bar) # value of instance variable will be printed i.e. 20
print(foo.get_bar()) # this will return the value of your private variable.
#also verify below
print(foo._Foo__bar) # value of private variable will be printed i.e. 10

Hope this clears your doubt.