Dundered global variable cannot be accessed inside a class method
Asked Answered
F

1

6

I am experiencing an obscure (to me) effect of dundered scoping, and trying to work out the rule for it:

#!/usr/bin/env python3

stuff = "the things"
__MORE_STUFF = "even more"

class Thing:
    def __init__(self):
        global __MORE_STUFF # doesn't help
        print(stuff) # is OK
        print(__MORE_STUFF) # fail!

Thing()

results in

$ python3 dunder.py
the things
Traceback (most recent call last):
  File "dunder.py", line 12, in <module>
    Thing()
  File "dunder.py", line 10, in __init__
    print(__MORE_STUFF) # fail!
NameError: name '_Thing__MORE_STUFF' is not defined

What is supposed to be a module-global variable is being treated as a class-level property - which, being undefined, is flagged as being undefined.

I've been trying to look through the documentation for this, but I cannot seem to work out what the rule is.

Can anyone point me to the appropriate documentation?

Ferneferneau answered 10/8, 2022 at 12:59 Comment(6)
docs.python.org/3/reference/expressions.html#atom-identifiersNotice
All such names in a class definition, not just attributes, are subject to name mangling. Don't use such class-private names at the module scope.Notice
What do you mean with "dundered"?Chercherbourg
@MarkRotteveel OP is referring to names that begin (but do not end) with a double underscore. ("Dunder" usually refers to names that begin and end with double underscores, though.)Notice
@Notice Thanks. I googled the term, but found nothing (other than suggestions that maybe I meant sundered).Chercherbourg
@MarkRotteveel docs.python.org/3/reference/… refers to them as "system-defined names", but acknowledges the informal term "dunder".Notice
N
4

The documentation refers to such names as class-private names:

__*

Class-private names. Names in this category, when used within the context of a class definition, are re-written to use a mangled form to help avoid name clashes between “private” attributes of base and derived classes. See section Identifiers (Names).

Since subclassing isn't something that applies to modules (at least, not to the extent that the language provides any tools or recommendations regarding how you might go about it), there's no need to use class-private names where _-prefixed names will do.

#!/usr/bin/env python3

stuff = "the things"
_MORE_STUFF = "even more"

class Thing:
    def __init__(self):
        print(stuff) # is OK
        print(_MORE_STUFF)

Thing()
Notice answered 10/8, 2022 at 13:8 Comment(2)
Thanks for the pointer. Is a bit implicit that the same effect applies to module-level variables, didn't expect that.Ferneferneau
Yeah, I've never seen that used; I'm not sure what the rationale is. I suspect it's easier, once the AST has been generated, to simply process all identifier nodes without having to identify attribute lookups?Notice

© 2022 - 2024 — McMap. All rights reserved.