How does the name mangling for a private class (class __ThisClass: pass
) works, if we want to use the class inside a @staticmethod
I am able to access it with __class__
(The class to which a class instance belongs). However, is there a way to use a mangled name?
class PublicClass:
@staticmethod
def foo(n):
if n>10:
return n
return PublicClass.foo(100) # <---
pub_obj = PublicClass()
print(pub_obj.foo(1), pub_obj.foo(11))
output: 100 11
Here PublicClass.foo(100)
can be used (or even __class__
)
class __PrivateClass:
@staticmethod
def foo(n):
if n>10:
return n
return __class__.foo(100) # <---
pri_obj = __PrivateClass()
print(pri_obj.foo(1), pri_obj.foo(11))
output: 100 11
if we use __PrivateClass.foo(100)
below exception is raised
class __PrivateClass:
@staticmethod
def foo(n):
if n>10:
return n
return __PrivateClass.foo(100) # <---
NameError: name '_PrivateClass__PrivateClass' is not defined
if we use _PrivateClass__PrivateClass.foo(100)
below exception is raised (replace the line# <---
)
NameError: name '_PrivateClass__PrivateClass' is not defined
Ask:
- How does the name mangling works for a private class?
- Should we (try to) never use the double underscore for the class name?
Note: Tried on Python 3.6.13 and Python 3.8.12
CodePudding user response:
Answering first question, in python you can't actually enforce privacy, the point is that it's only suggestion to be private. The python convention says to sugest private method/class use an _ (underscore) and double __ (underscore) is pseudo privacy that won't allow you to call it directly but still will be possibility to workaround it. About your example it doesn't make sense to call __PrivateClass.foo(100) inside this class, better would be just use self.foo(100) and not making it static method.
Answering second question, using underscore for anything is only suggestion that it should be private you can still call it like in example below:
class __PrivateClass:
def foo(self, n):
if n>10:
return n
return self.foo(5)
print(__PrivateClass().foo(100))
at the end use underscore only if you want something to be called only inside it's scope as a suggestion, have it in mind you can still call it outside but you shouldn't, probably there was some reason why it's private.
CodePudding user response:
As suggested in Python docs
This class object is the one that will be referenced by the zero-argument form of super(). __class__
is an implicit closure reference created by the compiler if any methods in a class body refer to either __class__
or super. This allows the zero argument form of super() to correctly identify the class being defined based on lexical scoping, while the class or instance that was used to make the current call is identified based on the first argument passed to the method.
When a name is not bound, an attempt to evaluate it raises a NameError exception. Private name mangling: When an identifier that textually occurs in a class definition begins with two or more underscore characters and does not end in two or more underscores, it is considered a private name of that class. Private names are transformed to a longer form before code is generated for them. The transformation inserts the class name, with leading underscores removed and a single underscore inserted, in front of the name. For example, the identifier __spam occurring in a class named Ham will be transformed to _Ham__spam. This transformation is independent of the syntactical context in which the identifier is used. If the transformed name is extremely long (longer than 255 characters), implementation defined truncation may happen. If the class name consists only of underscores, no transformation is done.
So whenever you are trying to call foo()
method with __PrivateClass.foo()
it automatically inserts the class name with it _PrivateClass__PrivateClass
But whenever you are using __class__
, this is resolved to <class '__main__.__PrivateClass'>
so we are not accessing attribute with class name instead we are accessing it as an object in current module.
Instead of using __class__
you can also access it like this:
import sys
class __PrivateClass:
@staticmethod
def foo(n):
if n>10:
return n
print(__class__)
print(getattr(sys.modules[__name__], '__PrivateClass'))
print(__class__ is getattr(sys.modules[__name__], '__PrivateClass')) # This will return True so you got it that __class__ is nothing but the current module and then __PrivateClass as its object
return __PrivateClass.foo(100) # <--- This line is raising exception because whenever we use __ inside class anywhere then that is transformed to **name_of_class_with_leading_underscore_stripped and single underscore appended** -> "_PrivateClass" and then double underscore and then the attribute name.
pri_obj = __PrivateClass()
print(pri_obj.foo(1), pri_obj.foo(11))
Also there is no such private class in python because when you insert double underscore in front of class then this will not affect any thing until you are not importing it somewhere
Also if you do the above thing using methods then also the same error will be there so there is nothing special to use staticmethod here
Let me know if this clears your doubt.