6

In Python 3, prefixing a class variable makes it private by mangling the name within the class. How do I access a module variable within a class?

For example, the following two ways do not work:

__a = 3
class B:
    def __init__(self):
        self.a = __a
b = B()

results in:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
NameError: name '_B__a' is not defined

Using global does not help either:

__a = 3
class B:
    def __init__(self):
        global __a
        self.a = __a
b = B()

results in:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 4, in __init__
NameError: name '_B__a' is not defined

Running locals() shows that the variable __a exists unmangled:

>>> locals()
{'__package__': None, '__name__': '__main__',
 '__loader__': <class '_frozen_importlib.BuiltinImporter'>,
 '__doc__': None, '__a': 3, 'B': <class '__main__.B'>,
 '__builtins__': <module 'builtins' (built-in)>, '__spec__': None}

[Newlines added for legibility]

Running same code in a module (as opposed to interpreter) results in the exact same behavior. Using Anaconda's Python 3.5.1 :: Continuum Analytics, Inc..

0

4 Answers 4

3

Apparently the "official" answer is not to use double underscores outside of a class. This is implied in the documentation here: https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references. Furthermore, the following (failed) bug report (and this response) make it explicit.

Sign up to request clarification or add additional context in comments.

Comments

2

It's ugly but You could access globals:

__a = 3
class B:
    def __init__(self):
        self.a = globals()["__a"]
b = B()

You can also put it in a dict:

__a = 3

d = {"__a": __a}

class B:
    def __init__(self):
        self.a = d["__a"]
b = B()

Or a list, tuple etc.. and index:

__a = 3

l = [__a]

class B:
    def __init__(self):
        self.a = l[0]
b = B()

14 Comments

I agree that this is ugly, but unless someone comes up with a better solution, it is the only one that seems to work so far. Thanks. I think that I'll ask this on the Python mailing list to see if it gets any different response.
@MadPhysicist, I added a couple of other ways to achieve the same, I cannot see any other way to avoid the name mangling
Looks like conceptually the same thing. You are storing a reference to __a in a non-mangled container outside the class first, whether globals() is doing it for you or you are doing it manually.
@MadPhysicist, it is, I just added it as accessing it gives you the option to name the list or dict with a name that may be more explicative of what is happening but you would probably want to use a weakref or dek d after so probably not worth the effort so I removed it.
My thoughts exactly. Sure there are workarounds, but the way I see it, either mangling is getting too greedy or global is not doing its job right.
|
-2

You are instantiating a class by passing a variable which is not defined. putting __a outside the class will not not work as the class will not see this variable. What you should do instead is:

__a = 3
class B:
def __init__(self, __a):
   self.a = __a
b = B(__a)

This way you would have passed an argument in the constructor for initializing.

1 Comment

A) You can use normal global variables the way I am doing. This one is causing a problem because of Python 3's name mangling, therefore B) I am not looking for a workaround but an actual solution that would let me use my global variable if at all possible.
-2

If you are going to mangle the names as you are trying to do then I would refer you to this article: http://shahriar.svbtle.com/underscores-in-python

As such, my solution to what you are trying to do is as follows:`

class R:
    global _R__a
    _R__a = 3
    def __init__(self):
        pass

class B:    
    global _R__a    
    def __init__(self):     
        self.a = _R__a
b = B()
print b.a
#3`

This way, you are also more specific about the variable you are calling without much room for modifying it later. Hope this works.

1 Comment

This does not answer my question at all. I am trying to access a module-level attribute with two underscores in the name inside a class method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.