Reputation: 15327
Issue [python 3.8.6]
def __foo():
pass
class MyClass:
@staticmethod
def bar():
__foo()
MyClass.bar()
results in:
NameError: name '_MyClass__foo' is not defined
Observations
If I replace __foo()
with foo()
, it runs fine.
I realize python is looking for __foo()
inside MyClass
. In C++, I can use ::__foo()
to explicitly invoke the function at the module level. Is there an equivalent in python?
Questions
Can you explain why I'm getting this error?
How can I keep __foo()
and invoke it from inside the Class?
RESOLUTION
I had a misunderstanding about naming conventions in python.
Private methods and symbols in classes have double underscore prefixes, but the same symbols at the module level have single underscore prefixes.
Upvotes: 1
Views: 60
Reputation: 96127
Because any name with two underscores as a prefix (and not a suffix) is mangled inside a class definition statement. From the docs
“Private” instance variables that cannot be accessed except from inside an object don’t exist in Python. However, there is a convention that is followed by most Python code: a name prefixed with an underscore (e.g.
_spam
) should be treated as a non-public part of the API (whether it is a function, a method or a data member). It should be considered an implementation detail and subject to change without notice.Since there is a valid use-case for class-private members (namely to avoid name clashes of names with names defined by subclasses), there is limited support for such a mechanism, called name mangling. Any identifier of the form
__spam
(at least two leading underscores, at most one trailing underscore) is textually replaced with_classname__spam
, where classname is the current class name with leading underscore(s) stripped. This mangling is done without regard to the syntactic position of the identifier, as long as it occurs within the definition of a class.
(emphasis added)
The sanest thing is not to name your function with two underscores. Alternatively, you can do something like this:
def __foo():
pass
def bar():
__foo()
class MyClass:
bar = staticmethod(bar)
MyClass.bar()
Or honestly, just keep bar
as a module-level function.
But again, using two-underscores doesn't make much sense. If you meant to signal that the function is not a part of the public api of the module, then you should use a single underscore:
def _foo():
pass
Upvotes: 2