Reputation: 214
I think I understand the concept of "name mangling" in python, but there's something that I probably missed. Take a look at the following code:
#!/usr/bin/env python
class Base(object):
__data = "Base"
@classmethod
def func(cls):
return "Class name is {}, data is {}".format(cls.__name__, cls.__data)
class A(Base):
__data = "A"
class B(A):
__data = "B"
print Base.func()
print A.func()
print B.func()
Here the output I get:
Class name is Base, data is Base
Class name is A, data is Base
Class name is B, data is Base
Now, I understand that for each class the actual name of the class attribute is mangled to _<Class name>__data
. So for instance, for Base it would be _Base__data
, for A it would be _A__data
, etc.
My question is, inside func
it identifies correctly the names of the inherited classes (Base, A and B), but cls.__data
always leads to cls._Base__data
. Why is that? I mean, if __name__
is A or B, then I know I'm inside class A or B, so I expect cls.__data
to be the one of A or B respectively. What am I missing here?
Upvotes: 3
Views: 2962
Reputation: 110476
You are not "missing", to the contrary, you just "found out" what name mangling does: it is made to ensure variables with double underscores inside a method will always see the attribute defined in the same class as that method, and in none of its subclasses.
If you simply want to use the attribute as it is overriden in each subclass, that is the normal behavior for all other attributes, but for the ones prefixed by two underscores.
So, what happens is that the .__data
name used inside func
is itself mangled, at compile time, to _base__data
.
OrderedDict
Python's collections.OrderedDict have an extra trap: Python offers both a pure-python implementation, which uses the __
for its "private attributes", as explained above, but also have a native code implementation in C, and the private structures of that are not exposed to Python.
And the collections
module ends the OrderedDict code block with these lines:
try:
from _collections import OrderedDict
except ImportError:
# Leave the pure Python version in place.
pass
That is: the normal "collections.OrderedDict" is written in C, with a lot of opaque structures, that can't be tapped in by subclasses.
The only way to have access to the Python defined OrderedDict is by
deleting the _collections.OrderedDict
attribute (in the _collections
, not collections
module), and reload the collections
module.
If you do that, and instantiate one ordered dict, the private data structures can be seem:
from imp import reload
import _collections, collections
backup = _collections.OrderedDict
del _collections.OrderedDict
collections = reload(collections)
PyOrderedDict = collections.OrderedDict
_collections.OrderedDict = backup
a = PyOrderedDict()
dir(a)
Out[xx]:
['_OrderedDict__hardroot',
'_OrderedDict__map',
'_OrderedDict__marker',
'_OrderedDict__root',
'_OrderedDict__update',
'__class__',
'__contains__',
'__delattr__',
...
]
Upvotes: 6
Reputation: 9998
As you have noticed, the name used for name mangling is the name of the class where a method is declared, not the derived type of the current object.
The documentation for this feature explicitly gives an example about protecting a variable from a derived class (rather than from external code using an instance variable).
Name mangling is helpful for letting subclasses override methods without breaking intraclass method calls. For example:
class Mapping: def __init__(self, iterable): self.items_list = [] self.__update(iterable) def update(self, iterable): for item in iterable: self.items_list.append(item) __update = update # private copy of original update() method class MappingSubclass(Mapping): def update(self, keys, values): # provides new signature for update() # but does not break __init__() for item in zip(keys, values): self.items_list.append(item)
Upvotes: 1