Unable to access module attribute with underscores in class method, Python 3

Steven D'Aprano steve at pearwood.info
Fri Jan 8 11:07:50 EST 2016


On Fri, 8 Jan 2016 03:14 am, Joseph Fox-Rabinovitz wrote:

> Hi,
> 
> I have a module attribute whose name starts with a pair of underscores.

Don't do that.

The way to flag a module-level variable as private is with a single leading
underscore. Double-leading underscores are intended for use in classes, to
be name-mangled, and double-leading-and-trailing underscores are reserved
for use by Python.


> I am apparently unable to access it directly in a class method (within the
> same module, but that is not relevant as far as I can tell). The following
> bit of code illustrates the situation:
> 
> __a = 3
> class B:
>     def __init__(self):
>         global __a
>         self.a = __a
> b = B()
> 
> This results in a NameError because of name-mangling, despite the global
> declaration:

The global declaration is name-mangled as well, as can be seen from the
byte-code:


py> from dis import dis
py> class Test:
...     def method(self):
...             global __a
...             x = __a
...
py> dis(Test.method)
  4           0 LOAD_GLOBAL              0 (_Test__a)
              3 STORE_FAST               1 (x)
              6 LOAD_CONST               0 (None)
              9 RETURN_VALUE


That's exactly as described: within a class, ALL references to
double-leading underscore names are mangled.


> Not using global does not make a difference. I posted a similar question
> on Stack Overflow, where the only reasonable answer given was to wrap __a
> in a container whose name is not mangled. 

That's not a reasonable answer. That's adding unreasonable complication to
the code, just to support an unreasonable global name.


> For example, doing `self.a = 
> globals()['__a']` or manually creating a dictionary with a non-mangled
> name and accessing that.
> 
> I feel that there should be some way of accessing __a within the class
> directly in Python 3. Clearly my expectation that global would fix the
> issue is incorrect. I would appreciate either a solution or an explanation
> of what is going on that would convince me that accessing a module
> attribute in such a way should be forbidden.

You are trying to fight the language. __names are intended for a specific
use in Python, and you're ignoring that and trying to use them for
something else. That's fine so long as your use and the intended use don't
clash, but if they do clash (as they are here) then there's only one
reasonable answer: stop abusing name-mangled names.

If you absolutely insist that you must must must continue to use a double
underscore name, you could also try this:

py> __a = 1
py> class Test:
...     def method(self):
...             x = eval("__a")
...             print(x)
...
py> Test().method()
1


But don't do that.



-- 
Steven




More information about the Python-list mailing list