Indentation in Python - Solution Option

In the vast majority of languages, if you remove all indentation in the entire source code of the program and then apply autoformatting, the program will remain fully operational and at the same time will be designed in the same style.

It would seem that in the case of Python, such an operation is not possible.

At least I couldn’t find anywhere how to instantly detect randomly offset indents in Python. I had to solve this problem myself.


Introduction


Dear Reader!
If the statement is close to you:
1) That programming is a high art.
2) That when programming in any language you need to make the most of the power and diversity of this language to minimize the source code.
3) That when programming, you need to show your high level in the source code of the program so that no one can say about your program that it is "dumb".

If at least one (!) Of the above points is close to you, then please do not read this article! Please do not waste your time on it.
It will talk about one completely far-fetched and absurd moment of programming.

Dear Reader!
If the statement is already close to you:
1) That programming is a routine, rather monotonous job, just like digging an endless winding ditch.
2) That when programming in any language, you need to use a certain minimum-optimal set of language commands (possibly discarding most of its commands) so that even a junior programmer can easily figure out your program!
3) That when programming your program should be to some extent “dumb”. So that after you implemented it, you could start a new project and calmly put the junior programmer who participated in the project to support and refine it under the regular small new requirements of the customer.

If all of these three statements above are true for you, then perhaps you have a problem, the solution of which I want to offer.

The main disadvantages of Python:

1) Python is "not minimized" in the use of resources and its data and, therefore, is not suitable for writing programs that require resource use, such as mobile applications, low-level programs (drivers, resident programs, etc.) .) etc.

2) Python is slow and single-threaded (GIL - Global Interpreter Lock).

3) In Python, program blocks are based ONLY (!) On indentation. Because of this:

  • the “readability” of programs decreases (see below);
  • full autoformatting of the source text is impossible;
  • there is a likelihood of an error occurring with an accidental and unnoticed offset of indents, which is sometimes very difficult to find and correct.

The first drawback of Python is difficult to smooth out, and here it just has a limitation in its scope. But this is a natural moment, because it is impossible to come up with a language that would be most effective in all areas of the tasks.

The second drawback of Python is that it has excellent two-way interoperability with C / C ++.

Often a successful project is developing quite rapidly. And at first there are no high performance requirements, and in Python, tiny inserts in C / C ++ are used if necessary.
But as the project develops and expands, performance requirements increase accordingly, and Python more and more often begins to fulfill the functions of the called language from C / C ++. At the same time, the role of Python itself does not decrease, as when programming sections that do not require high execution speed (and there are usually quite a lot of them in large projects), Python is a more convenient tool than C / C ++.

And for the third drawback of Python, I would like to offer my own solution.

You all know that for the vast majority of languages ​​the source text auto-format is very often used.

Those. no matter what indentation a program is written in this language, when you start autoformatting, all indentation will be brought to its standard form. For Python, this seems impossible.

Any programmer who has written projects in several thousand lines of the language knows that the development process is not just inventing the next piece of the program and filling it in, but constantly moving sections of the code to the subprogram, now between the program blocks, then to common external modules, etc. P.

Moreover, in practice, users / customers already at the initial stage, having worked with the first outline of the program, begin to change and supplement the tasks of the final project, which leads to a strong adjustment of the source code.

And then in Python, in the event of a significant change in dozens of pieces of someone else’s (!) Program (or your own, but which you don’t remember at all), it’s worth while accidentally catching an extra piece of code belonging to another block when transferring a block, the program may remain fully operational, but the algorithm of its work will change (read “the program will start to work incorrectly”), and finding the place of such an error in someone else’s program and then correctly restoring the indentation is sometimes very difficult!

In this case, you can, of course, look at the source text (before the changes), but if you have already made a lot of corrections in this place, you will have to “unwind” this whole chain of corrections.

Solving the indentation problem in Python


Indentation in Python is formed by the following commands:

- class
- def

- for
- while

- if

- try

- with

And to remove the dependence on indentation, for each of these commands I decided to make it a rule to use the “final” command, which will definitely close the block of commands (indentation).

Class and def Commands


For class / def commands, there is usually no problem completing the indentation block, as the block is closed with the new class / def command.

The only case is the presence of one or more subprograms declared inside another subprogram / method.

def ppg_1():
    def ppg_2():
        ...  ppg_2 ...
    def ppg_3():
        ...  ppg_3 ...
        ...  ppg_3 ...
        ...  ppg_3 ...
    ...  ppg_1 ...

Then, if you accidentally shift the block of instructions of the last internal subprogram, then it will merge with the commands of the subprogram / method in which this internal subprogram is declared.

def ppg_1():
    def ppg_2():
        ...  ppg_2 ...
    def ppg_3():
        ...  ppg_3 ...
    ...  ppg_3 ...
    ...  ppg_3 ...
    ...  ppg_1 ...

In this case, at the end of this internal routine / method you just need to put “return”, which will clearly indicate the end of the block of its commands.

def ppg_1():
    def ppg_2():
        ...  ppg_2 ...
    def ppg_3():
        ...  ppg_3 ...
        ...  ppg_3 ...
        ...  ppg_3 ...
        return
    ...  ppg_1 ...

The for and while commands


At the end of the "for" and "while" statements you need to put "continue".

Those. the command will look like this:

for  <...> :             #  
    ...  ....
    continue             #  

and

while  <...> :           #  
    ...  ....
    continue             #  

For instance:
        ...  ...

        for i in range(10):
            ...  for ...

            ...  for  ...

            ...  for  ...

        ...  ...

Accidentally deleting the indentation in the last commands of the “for” block does not lead to a program execution error, but leads to erroneous results! And it’s very difficult to find such a failure if you don’t know the algorithm of the program (for example, if it’s the program of a retiring colleague)!

Here's how:
        ...  ...

        for i in range(10):
            ...  for ...

        ...  for  ...

        ...  for  ...

        ...  ...

And in the example below, accidentally deleting the indent will immediately throw an error:
        ...  ...

        for i in range(10):
            ...  for ...

            ...  for  ...

            ...  for  ...

            continue

        ...  ...

If command


At the end of the if statement you need to put the command “elif 0: pass”, and instead of the “else” use the command “elif 1:”.

Those. for "if" there will be a complete block of commands:

if <>                      #  
    ...  ....
elif <>
    ...  ....
elif 1:                    #  "else"
    ...  ....
elif 0: pass               #  

For instance:
        ...  ...

        if  result != -1 :

            ...  if ...

            ...  if ...

            ...  if ...

        elif 0: pass

        ...  ...

If you make it as shown above, then in the “if ... elif 0: pass” command block, indentation will generate a startup error.

Without “elif 0: pass”, if then the indents in the last lines of the “if” block are accidentally deleted, you will first look for the place that caused the program to start working incorrectly, and then think about which indents should be in the block and which ones - no.
        ...  ...

        if  result != -1 :

            ...  if ...

        ...  if ...

        ...  if ...

        ...  ...

Why else, in my opinion, it is advisable to close the blocks of commands created by the operators "for",
"while", "if", etc ...

Because when you look at a section of code where there is a big difference in the level of
indentation between the very end of the current block and the beginning of the next, you often can no longer understand which indentation belongs to what.

Then the constructions with “continue” and “elif 0: pass”, in addition to protecting against accidental deletion, will also allow you to indicate which unit is started and write a comment on it.

For example, you see the end of a large block:

                            ...  ...

                            ...  ...

                            ...  ...

                            ...  ...

                        ...  ...

                        ...  ...

                        ...  ...

                        ...  ...

    elif result == 1 :

        ...  ...

        ...  ...

        ...  ...

        ...  ...

And it's hard to remember what each indentation level means.

But it’s much easier when it looks like this:

                            ...  ...

                            ...  ...

                            ...  ...

                            ...  ...

                            continue    #   b'\\r\\n'   

                        ...  ...

                        ...  ...

                        ...  ...

                        ...  ...

                    elif 0: pass     #  " "  " "

                elif 0: pass         #   . - 

                continue             #    

            continue                 #  .,  -  " "
                      
    elif result == 1 :

        ...  ...

        ...  ...

        ...  ...

        ...  ...

Try command


There is a complete analogy with “if”.

try:                   #  
    ...  ....
except <...>:
    ...  ....
except 1:              #  "else"
    ...  ....
except 0: pass         #  

The only problem is the finally: command . After it, you can no longer put any of the commands of the current try-block.

Therefore, if there is a need to use it, then to preserve the possibility of auto-format and protect against accidentally deleting indents, you need to remove the entire command block after “finally:” in the local subprogram (that is, declare it as a subprogram inside the current subprogram).

Those. the text with "finally:" will be like this:

    def my_ppg():
        ...
        return

    ...

    finally:
        my_ppg()

    ...

In this case, you can also safely apply autoformatting and not be afraid of
accidentally deleting indents.

"With" command


There are no additional commands for “with” in Python that could serve as the end of a block. Therefore, the situation with is similar to the situation with finally.
Those. either we transfer all the commands that are executed in the "with" statement block to the local subroutine, or ... But then I’ll say a terribly blasphemous thing: "... or you just never need to use it."

The fact is that “with”, on the one hand, is just a “wrapper” for existing Python commands (that is, you can always replace it with a similar set of commands), on the other hand, practice has shown that for junior programmers this context The manager is difficult for full development. And therefore, if you want a junior programmer to calmly accompany the implemented project after you, then you do not need to use teams in the project that impede its work.

Conclusion



I think you already understood that if you write a program in Python using ANYWHERE (!) Of the above techniques for creating indentation blocks, it is quite easy to write a FULL AUTO FORMATTING such a program even if you skew or completely remove the indents in it, because for all command blocks there is a beginning and end of the block, independent of indentation.

And now, with a smile, we pose the question like this: “What are the disadvantages of Python if you correctly format indentation blocks, if necessary interact with C / C ++ and not use Python in mobile and resource-critical applications?”

Answer: “Only minor flaws. Those. by and large - no. ”

And with such a formulation of the question, we can only enjoy the main advantages of Python.

  1. Simplicity.
  2. Minimum testing cycle of sites: wrote-launched-checked.
  3. Power - libraries / frameworks for Python are "for every taste and color."

These three advantages together give, in my opinion, an almost perfect version of the language (do not forget that this is only subject to the conditions specified in the question above!).

All Articles