5

Why does this code fail if there is no cls. before __TEXT

__TEXT = "abcde"

print(__TEXT)
class aaa():
    @classmethod
    def bbb(cls):
        print(__TEXT)

aaa.bbb()

The output is:

abcde
Traceback (most recent call last):
  File "<string>", line 9, in <module>
  File "<string>", line 7, in bbb
NameError: name '_aaa__TEXT' is not defined

If you make __TEXT a class variable and try to reference it without the class prefix as follows:

class aaa():
    __TEXT = "abcde"
    @classmethod
    def bbb(cls):
        print(cls.__TEXT)
        print(__TEXT)

x = aaa()
x.bbb()

You get the same error but it doesn't make sense:

abcde
Traceback (most recent call last):
  File "<string>", line 10, in <module>
  File "<string>", line 7, in bbb
NameError: name '_aaa__TEXT' is not defined
pauleohare
  • 209
  • 2
  • 8
  • Does this answer your question? [What is the meaning of single and double underscore before an object name?](https://stackoverflow.com/questions/1301346/what-is-the-meaning-of-single-and-double-underscore-before-an-object-name) – JonSG Feb 19 '22 at 19:47
  • 1
    It's interesting, as I can't think of any reason why a local variable (as opposed to an attribute) needs to be subjected to name mangling. Perhaps to account for attribute access via `getatter` et al. rather than dotted syntax alone. – chepner Feb 19 '22 at 19:55
  • @chepner Thats the question. Why would python mangle a module variable/attribute with the class name? – pauleohare Feb 20 '22 at 16:30
  • 1
    It might not be intentional. It may have been simpler to catch all identifiers, whether or not used in attribute access, and this is just a (relatively) harmless side effect. – chepner Feb 20 '22 at 16:36
  • (A case of the reference implementation driving the specification, as it were.) – chepner Feb 20 '22 at 16:37
  • @chepner I'm sure it's not intentional. A bug maybe? – pauleohare Feb 20 '22 at 17:00
  • @pauleohare Not in the sense you are thinking. It's not just that they are subject to name mangling, but they are *documented* as being subject to name mangling. The documentation explicitly says that *identifiers* (not just attribute names) are re-written. – chepner Feb 20 '22 at 18:59

4 Answers4

1

In PEP 8, they specifically say:

__double_leading_underscore: when naming a class attribute, invokes name mangling (inside class FooBar, __boo becomes _FooBar__boo ...).

Notice the selection in bold. In the exact same sentence where they draw attention to mangling's use in naming class attributes, they state that the mangling happens to any __boo inside of the class, which perfectly explains what you are observing.

Kraigolas
  • 5,121
  • 3
  • 12
  • 37
0

Not quite sure if that was the error, but you can just make the function require a parameter text, that seems to work just fine. You need to give me more information though so I can try to help.

    __TEXT = "abcde"

    print(__TEXT)
    class aaa():
    @classmethod
    def bbb(self, __TEXT):
       print(__TEXT)

    aaa.bbb(__TEXT)
  • Thanks but the code is just a simple example of a module level variable/attribute being mangled with the class name. – pauleohare Feb 20 '22 at 16:28
0

A double underscore prefix causes the Python interpreter to rewrite the attribute name in order to avoid naming conflicts in subclasses.

This is also called "name mangling." The interpreter changes the name of the variable in a way that makes it harder to create collisions when the class is extended later.

Reference docs: https://docs.python.org/3/tutorial/classes.html#private-variables

0

The Python Interpreter mangles variable-names with a double-underscore to avoid name clashes with variable-names defined by subclasses.

The goal behind this is almost equivalent to final variables in Java and non virtual in C++.

Take for instance:

class Human:
    def __init__(self):
        self.surname = 'Jeffrey'
        self._age = 22
        self.__id = 5

# Let's check the variables associated with this class
x = Human()
print(dir(x)) # gives: ['_Human__id', ..., '_age', 'name']

# Create a subclass 'Cook'

class Cook(Human):
    def __init__(self):
        Human.__init__(self)        
        self.__id = 25

y = Cook()
print(dir(y)) # gives: ['_Human__id', '_Cook__id', ..., '_age', 'name']
Bialomazur
  • 1,122
  • 2
  • 7
  • 18
  • Do you feel this is not a duplicate of : https://stackoverflow.com/questions/1301346/what-is-the-meaning-of-single-and-double-underscore-before-an-object-name ? – JonSG Feb 19 '22 at 19:56
  • @JonSG feel like it's a bit more specific, the linked question is more general – Bialomazur Feb 19 '22 at 19:56
  • Did you check out the example class in the accepted answer? It is basically the same as you have documented with `Human()`. If you don't feel it is a duplicate, that is of course your prerogative. – JonSG Feb 19 '22 at 20:00
  • Thanks but I don't think this answers the question as the variable is not a class variable it is at module level so subclasses won't even see it. – pauleohare Feb 20 '22 at 16:25