0

Why does the val_n function return an older value of self.__n? Seems to me that if obj.__n has been updated to 3, calling self.val__n should return 3 and not 2.

class myClass:
    def __init__(self,n=1):
        self.__n=n
    def val_n(self):
        return self.__n

#create an instance, __n is 2
obj=myClass(2)

#update __n to 3
obj.__n=3

#verify obj.__n has changed from 2 to 3
print(obj.__n)

#why does this still return 2?
print(obj.val_n())
HMReliable
  • 871
  • 5
  • 11
  • 1
    The duplicate doesn't address why `obj.__n = 3` seems to change the value. The answer is that this assignment, as it does not occur inside a method definition, creates a *new* attribute whose name really is `__n`, as opposed to `_myClass__n`. Use `dir(obj)` to show that attributes by both names exist after the assignment. – chepner Jul 27 '19 at 18:01
  • @chepner yes, agreed. this question is not really a duplicate. this question is much more straightforward, and doesnt require elucidating the difference between public and private methods. – HMReliable Jul 27 '19 at 18:06

1 Answers1

2

This is based on Python’s name mangling feature. Using a leading double underscore for class attributes means Python will use name mangling.

class X:
    __attr = None

x = X()
x.__attr
# raises AttributeError
x._X__attr
# None

Python turns the name __attr in class X into _X__attr so that when subclassing, this attribute is less likely to be overwritten if a subclass also defines a __attr but the parent class’ attribute is needed.

When you do x.__n = 3, you’re not overwriting the attribute defined in the class since this has become x._myClass__n to any outside accessor. You are instead creating a new attribute (and Python doesn’t really check x.__n before assigning it to see if it’s already an attribute of the object - which is why no AttributeError is raised).

N Chauhan
  • 3,407
  • 2
  • 7
  • 21