Understanding the naming mechanism of Python

  • 2021-12-05 06:57:26
  • OfStack

Catalogue 1, Preliminary Exploration 2, Mystery 3, Inquiry 4, Truth

Guess the output of the following program:


class A(object):
       def __init__(self):
              self.__private()
              self.public()
       def __private(self):
              print 'A.__private()'
       def public(self):
              print 'A.public()'
class B(A):
       def __private(self):
              print 'B.__private()'
       def public(self):
              print 'B.public()'
b = B()

1. Preliminary study

The correct answer is:


A.__private()
B.public()


If you have guessed it correctly, you can stop reading my blog post. If you are not guessing correctly or have doubts in your heart, then this blog post of mine is just for you.

1 Why is it output “A.__private()” Here we go. But to explain why, it is necessary for us to understand 1 Python Gets or sets the naming mechanism for.

According to Python manual The variable name (identifier) is an atomic element of Python. When a variable name is bound to an object, the variable name refers to that object, just like human society 1, right? When the variable name appears in the code block, it is a local variable; When the variable name appears in the module, it is a global variable. Modules are believed to be well understood by everyone, but code blocks may be confusing. Explain 1 here:

The code block is a section of Python program text that can be used as an executable unit; Modules, function bodies, and class definitions are all blocks of code. Not only that, but every interactive script command is also a code block; A script file is also a code block; A command line script is also a code block.

Next, to talk about the visibility of variables, we introduce the concept of a range. Scope is the visibility of variable names in code blocks. If a local variable is defined in a code block, the scope includes this code block. If a variable is defined in a function block, the scope extends to any 1 block in that function block unless another 1 variable with the same name is defined. However, the scope of variables defined in a class is limited to the class code block, and does not extend to the method code block.

2. Lost Trace

According to the theory in the previous section, we can divide the code into three code blocks: the definition of class A, the definition of class B and the definition of variable b. From the class definition, we know that the code defines three member variables for the class A ( Python The function of is also an object, so it is also feasible to call the member method a member variable.); Class B defines two member variables. This can be verified by the following code:


>>> print '/n'.join(dir(A))
_A__private
__init__
public
>>> print '/n'.join(dir(B))
_A__private
_B__private
__init__
public

Gee, why does the class A have a name called _A__private Adj. Attribute What about? And __private Disappeared! This is about talking Python The private variable of.

3. Explore

Understand Python My friends all know that Python Treats variables that begin with two or more underscore characters and do not end with two or more underscores as private variables. Private variables are converted to long format (become public) before code generation. The conversion mechanism is this: insert the class name at the front of the variable, and then add an underscore character at the front. This is called private variable rolling ( Python0 ). As in the class A __private The identifier will be converted to _A__private This is what appeared in the previous section _A__private And __private The reason for disappearing.

Let's talk about two digressions:

1 is because rolling will make the identifier longer, and when it exceeds 255 When, Python Will be cut off, pay attention to the naming conflict caused by this. 2 is when all class names are underlined, Python Rolling is no longer performed. Such as:

>>> class ____(object):
       def __init__(self):
              self.__method()
       def __method(self):
              print '____.__method()'
>>> print '/n'.join(dir(____))
__class__
__delattr__
__dict__
__doc__
__getattribute__
__hash__
__init__
__method              #  Not crushed 
__module__
__new__
__reduce__
__reduce_ex__
__repr__
__setattr__
__str__
__weakref__
>>> obj = ____()
____.__method()
>>> obj.__method()      #  Can be called externally 
____.__method()

Now let's go back and see why we output " A.__private() "Come!

4. Truth

I believe smart readers have guessed the answer now. If you haven't thought of it, let me give you a hint: the truth is similar to macro preprocessing in C language.

Because the class A defines a private member function (variable), the private variable pad is performed before the code is generated (notice the red line in the previous section?) . After rolling, the code of class A becomes like this:


class A(object):
       def __init__(self):
              self._A__private()          #  This business has changed 
              self.public()
       def _A__private(self):           #  This business has changed, too 
              print 'A.__private()'
       def public(self):
              print 'A.public()'

Is it a bit like the macro expansion in C language?

Because there is no override when the class B is defined __init__ Method, so A is still called .__init__, That is, it executes self._A__private(), Natural output " A.__private()” It's over.

The following two pieces of code can increase persuasiveness and improve understanding:


>>> class C(A):
       def __init__(self):          #  Rewrite __init__ Is no longer called self._A__private
              self.__private()       #  What is bound here is _C_private
              self.public()
       def __private(self):
              print 'C.__private()'
       def public(self):
              print 'C.public()'
>>> c = C()
C.__private()
C.public()
############################
>>> class A(object):
       def __init__(self):
              self._A__private()   #  Call 1 Undefined functions, Python Will give it to me  ^_^ ~ 
              self.public()
       def __private(self):
              print 'A.__private()'
       def public(self):
              print 'A.public()'
>>>a = A()
A.__private()
A.public()

Related articles: