title: Private attribute name mangling inheritance
body: <p>This is an example from <a href="https://effectivepython.com/" rel="nofollow noreferrer"><em>Effective</em> Pʏᴛʜᴏɴ</a>, that I am clearly missing something on. I added a few print's to help convince myself, but I'm still a little unclear.</p>
  <p>I understand that when you attempt to access an inherited private variable, it fails because in the instance dictionary of the child, the name has been mangled (last line below, attempting to access <code>a.__value</code> rightfully fails because the instance dict contains the mangled version <code>_ApiClass__value</code>).</p>
  <p>Where I'm getting tripped up is why the inherited method <code>get</code> doesn't have this issue.  If you print <code>self.__dict__</code> in the call to <code>get</code>, you can see that we're still working with the same Child instance dict as we would be if we attempted to use dotted access directly from the child (which contains the mangled name only).  Dotted attribute access from within this method somehow properly translates to the mangled name and retrieves the private variable.</p>
  <p>My understanding of attribute access is that under the covers, what is essentially happening (albeit simplified) is <code>a.__value</code> is basically <code>a.__dict__['__value']</code>.  This makes sense and is proved out when you attempt to directly access the inherited private variable, as it fails since only the mangled name is in the Child dict.  However the inherited method <code>get</code>, which is operating on the same instance dict from Child, works with dotted access so it is clearly not doing  <code>a.__dict__['__value']</code>, but rather <code>a.__dict__['_ApiClass__value']</code>.</p>
  <p>What is the distinction here that causes the private attribute access from within the <code>get</code> method to be aware of the mangled name as opposed to similar attribute access from the child?</p>
  <pre><code>class ApiClass():
      def __init__(self):
          self.__value = 5
  
      def get(self):
          print(self.__dict__['_ApiClass__value']) #succeeds
          print(self.__dict['__value']) #fails bc name mangle
          return self.__value # How is this translated to '_ApiClass_value'
                              # but a similar instance lookup fails?
  
  class Child(ApiClass):
      def __init__(self):
          super().__init__()
          self._value = 'hello'
  
  a = Child()
  print(a.__dict__)
  print(a.get())   # Works, but instance dict has no '__value' key? 
  print(a.__value) # Fails because name mangled to '_ApiClass_value'.
  </code></pre>