2

Suppose we have an advanced class that implements a lot or special functions, including

__getattr__
__getitem__
__iter__
__repr__
__str__

An example would be ParseResults class in pyparsing.py. Removing the (no doubt important) details, the constructor looks like this

def __init__( ... ):
        self.__doinit = False
        self.__name = None
        self.__parent = None
        self.__accumNames = {}
        self.__asList = asList
        self.__modal = modal
        self.__toklist = toklist[:]
        self.__tokdict = dict()

And then we have __getattr__ and __getitem__:

def __getattr__( self, name ):
    try:
        return self[name]
    except KeyError:
        return ""

def __getitem__( self, i ):
    if isinstance( i, (int,slice) ):
        return self.__toklist[i]
    else:
        if i not in self.__accumNames:
            return self.__tokdict[i][-1][0]
        else:
            return ParseResults([ v[0] for v in self.__tokdict[i] ])

I'd like to be able to do something like print res.__tokdict in the console, but that would not work: it prints empty string, as the __getattr__ implementation should.

How do I work around this and see the actual data in the object?

  • 1
    try `my_instance._ParseResults_tokdict` – juanpa.arrivillaga Aug 24 '17 at 14:18
  • @juanpa.arrivillaga tried, same thing –  Aug 24 '17 at 14:24
  • 2
    That would be `my_instance._ParseResults__tokdict` (mind the double underscore). What is happening here is [name mangling](https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references). **Edit:** You can also check `dir(my_instance)` to see what's actually in the instance's `__dict__` – plamut Aug 24 '17 at 14:26
  • @plamut Double underscore did the trick, thank you. Also, thanks for linking to name mangling. You can now close this as dupe of the other question that explains name mangling, or convert your comment to an answer. –  Aug 24 '17 at 14:31
  • WIll do, just give me a few minutes :) – plamut Aug 24 '17 at 14:31
  • @Arkadiy yep - was missing the extra underscore. Wrote that comment on my phone :) – juanpa.arrivillaga Aug 24 '17 at 15:31

1 Answers1

2

What is happening here is name mangling. Quoting the documentation:

Any identifier of the form __spam (at least two leading underscores, at most one trailing underscore) is textually replaced with _classname__spam, where classname is the current class name with leading underscore(s) stripped.

This mechanism is used to prevent the subclasses from accidentally overriding an attribute from a base class.

In our particular case, the __tokdict attribute that is set in ParseResults.__init__() can thus be accessed as my_instance._ParseResults__tokdict.

Quick example:

>>> class Foo(object):
...   def __init__(self, x):
...     self.__x = x
... 
>>> f = Foo(7)
>>> f._Foo__x
7
>>> f.__dict__  # let's see what's inside the instance
{'_Foo__x': 7}
plamut
  • 3,085
  • 10
  • 29
  • 40