Dissecting Python inside out
Table of Contents
Dedication
For my tiny family
Relevant quotes
“I can calculate the motion of heavenly bodies but not the madness of people.”
- Isaac Newton
Programs must be written for people to read, and only incidentally for machines to execute."
- Abelson & Sussman, SICP, preface to the first edition
"Short words are best and the old words when short are best of all."
- Winston Churchill
“The glass is neither half empty nor half full. It’s simply larger than it needs to be.”
- Grace Hopper
“Manage things. Lead people.”
- Grace Hopper
Preface
Figure 1: An older tutor
This book is about programming machines to execute precisely what humans ordered them to do. It is the same in intention like a slew similar books already written about the same subject. And it is a completely wrong first impression.
It is a fiction book in a wide sense of this word: it has an objective, the subjects and the plot. The principal objective in this book is Python programming language and its usage by humans.
The subjects are students, teachers, books, programmers, software engineers and developers, other professionals related to programming. The plot is a misleading: its natural prerequisites caused by human nature, its past and present in software industry.
Followed by a set of plain deductions it might unlock for a literate
reader a realm of programming
. It is a realm
actually due to the
strict set of certain rules defined by his Majesty Python.
Learning these rules one by one is the only proper way to succeed in
studying programming language as a system.
All what I might propose to you is to serve you as a bit experienced and elder guide on your way to acquire precious knowledge. And it will be a small nudge in the right direction. All what I might propose to you is a fun, joy and glee on this path only. Nothing more, nothing less.
Keep tighter, please.
For boring reader
The task isn't so tough it might seem because the major rules are
naturally entangled. There are only a thriple primals: True
,
False
and None
. The rest are derivatives based on the them.
Undoubtedly it prunes down the lesson. All what you need is to build the tree-like structure of obtained lore in your mind. Of course it requires from reader a lot of perseverance and diligence to achieve a true knack in programming, but it is definetely not a rocket science!
Naturally there are several formal syntactical rules, but all them are naive and would be learn without any effort at all.
Style Notes
A major killer feature of this book is its style: it is not yet
another user manual, journal or TTY input/output spiced with
comments. It might deem a bit strange though. What if the book will
be directly written in programming language
? Initially you will
wittingly immerse in a full-dressed Python-written program text,
the valid program, which might be successfuly executed by
machine, I mean.
Another noticeable discrepancy is a test-driven approach in disclosing programming language features. All material being wrote down as a chain of tests from beginning to the end. Generally speaking the next one based on the previous and you should incessantly compare them one against each over, detect the difference and make conclusions by your own.
In addition I strive to compose each program in a paramount clarity
and density in expectation that instantly the reader
will being
capable to gaze its incredible power and naked beauty from
inside.
The main intention of this book is to take to the reader a chance
to be fun while reading. At the another hand it might be impossible
to write a giddy novel for human readers and an absolutely precise
set of instructions for computer at the same time. Bear it in mind
the text
deliberately splitted into two main chunks by its
purpose: the comments and the program itself. The comments are
for humans only, the program is for two sides of software industry:
it is human-readable and machine-executable simultaneously.
The side effect
The side effect might be a cramming gap during learning. All material compounded into a monolith by a logical intersection. Thus it is highly welcomed to follow the sequence of chapters by its nature, from first and foremost to the second one, the third etc.
A multiplied repetition in hope to rehearse the chunk of studied material verbatim and forget it forever — short, attractive but lousy way to achieve knowledge. It is a cognition trap. And yes, it is a dogged determination in self-deception. It is an eloquent illustration of redundant, tedious and, in same time, popular way to do things wrong.
All what you are need in is to delve into Python depths leading by an experience guide. That's it.
All articles are short enough to being grasped less than 25
minutes. Self-pace is highly welcomed: going forward only after
you sure that know the present point as a back of your hand. No
need to subdue yourself to read down and down without any
comprehension. It is not a learning at all, it is a foolish
torture. Slow your pace, please. You can't fathom it all of a
sudden
.
A minuscule free time in the early morning before yours breakfast would be an ideal time for learning programming. It is not a joke, guys, moving forward.
General observations:
Machine Code vs Source Code vs Program
Initially at the uprise of programmable machines there was the single set of plain instructions embedded into the automaton. This set was defined by a creator of this machine once, being incompatible with other machines, so it was a life-long program solution for each particular mechanism only.
Further technical evolution produced a new demand: it might being convenient if one particular mechanism obey different set of instructions and thus produce a various sequence of operations. In other words it was demand to easily reprogram machine to do something new.
At programming early beginning there was a machine code only. It is a kind of machine's language by its purpose, thus casual programming obliged an operator with a true wit and grasp in translation from human language into machine code.
The idea of compiler as a middleware between humans and machines exactly for automatic translation was on the tip of the tongue at those days, but implemented by Grace Hopper's titanic efforts and phenomenal persistence only in 50s. It was the basement for all modern high-level programming languages establishment. Since that time and forever it is possible to write program not in machine code only, but utilizing some particular programming language which by its purpose is much human-friendly.
As you might expected what bright win of human ingenuity and infinite lucidity all the sudden became a natural feature in the industry, and it is a wrong conclusion: an old style of writhing machine code begot a source code term initiation and popularization. But what this source code ever means?
It is a code by the definition and it is a source of machine
code before compilation by the intention. These peculiar niceties
allow to write programs as a code, neglecting the most
significant feature of high-level programming language - its
readability, and name this pseudo-program as a source code.
Yes, it is wierd, and in a few cases this transference is
convenient: it permits to type less, to comment nothing at all, to
name variables absolutely meaningless like bar
, foo
, or even
a, b, c, d, e, f, g
!
Yes, seems like things go wrong there: for some vital reasons all specialist in software industry still write only the source code and shrink to switch on writing programs instead. I observe like it goes on and on for several decades already. And seems like nobody capable to take a challenge to erradicate this distortion except me.
Debugging is a major time-consuming task
Figure 2: Time and efforts spent on debugging
for a casual software developer and often it consumes up to 40% of their efforts to develop and maintain software nowadays.
Cognitive dissonance and size of manuals
Figure 3: The average manual for proficiency
“We do not describe the world we see, we see the world we can describe.”
— Rene Descartes
At one hand we have a relatively simple job: to tame a machine to execute the precise sequence of operations utilizing the programming language clear enough to be understood by humans and machines. At another hand we might notice a plethora of manuals, tutorials and intimidatingly large books which take an attempt to dismantle the programming as a professional skill using a plain English commonly and a handful of real code snippets as an unavoidable exclusion out of this rule.
A hidden complexity
It is straightly inherent to a human nature. Even an experienced professional software developer often can't explain what the machine should do even in a plain English. Misleading in human language should spawn only erroneous decidions with the following often wrong conclusions. The slogan "Keep clear" is a paramount goal of most humans, it is difficult to detour the brain fog after the some time spent in mulling around something enough tough.
There are tons of material and most of it represents programming
language
in an imperative style.
An average age of readers
Figure 4: The team
“Youth is easily deceived because it is quick to hope.”
— Aristotle
The median age in a casual team of software developers is something below 30 years. Yes, this is a pledge of physical health, mental flexibility and, in most cases, a squad emotional stability. At the other hand it is too young to be experienced enough to perceive reality as a sum of events in the past.
Yes, 30 years old for human being intellectually is like a new born toddler physically. They capable to do anything, but it requires time and effort to learn it up. Nowadays they're learning a modern programming by tutorials, manuals and books. That's right.
But, how these learning material is written and why? Why it is so intimidatingly large?
Testing as an innate tool to learn for humans;
“There are no facts, only interpretations.”
- Friedrich Nietzsche
Figure 5: Toddlers playing
If I ask you what you see on the picture above your average answer would be: “There are two small boys playing cubes together.” Right? But what if I tell you: “These two boys are conducting a series of test to cognate the hidden features of cubes. The older one demonstrating his knowledge in this particular area, and the youngest one assisting him and learning by testing.”
This is a simple and straight evidence of human innate ability to learn. And it requires no words at all, just attention, patience and perseverance to deduce the meaning of this show.
Well, it is about playing cubes, right? Not at all. It is a general approach to learning: an experience tutor should demonstrate you some particular niceties by a sequence of obvious and explicit tests. All you have to do are observe them, take them into consideration, catch the essence out of them and put results into your memory.
Unit testing in CS
It was initially introduced by computer scientists in 80s, and became a wide-spread and popular concept in the industry only in the beginning of 21st century. Thus, it is easy to deduce that initially almost all modern programming languages, which was developed earlier this date, was happily built without any unit testing at all.
Yes, it was.
Einstellung effect or golden hammer
“If the only tool you have is a hammer, one tends to treat everything as if it were a nail.”
- Abraham Maslow
or cognition inertia in writing book about programming. First books about programming were written in a plain English and it was ample at that time: there wasn't any programming languages, the large set of hexadecimal commands only. Later, when full-fledged programming languages was introduced, authors predictably included wee chunks of programs into the text of books about programming by these languages. It's right. It is much more illustrative and comprehensible for readers.
When later unit testing was introduced it consider as an extra special concept in software development. What if I ask you: “What should be the next step to get rid of coding and switch on writing programs bear in mind all these observations above?”
It should be the programs as the textbook, written in particular programming language, with plentiful number of tests to examplify concepts, ideas, approaches hidden behind the wall of mutual understanding between a man and machine.
Figure 6: Brain fog
Reading as a tourture
Avoidance of source code reading (escapism); Actually it is a downright grueling task to do
A harsh deduction
Figure 7: Test failure
“Doubt is the origin of wisdom”
— Rene Descartes
Summarize a handful of penetraiting observations above it might be dedused:
The paramount goals of writing are absolute clarity and density, so it might sound a quite strange. In reality, it is much complex task than it seems and a plethora of already written manuals about programming is a strict evidence to interpret this as a spoiled approach. In contrary: “Why men avoid reading and study some common things as programming in just one book? Is it really so boring?”
My grandma told me “Eugene Onegin” in just three evenings. She knew each string in the poem's sequence(!) It was so astounding and 40 years later I still remember it. Not the poem itself! The remembrance of no-limit boundaries for human mind. Now I'm can conclude that only natural language is enough complex and complicated to transmit such long message for a such long time.
30 years ago, in 1991 I became a programmer. My tool was a quite advanced micro-calculator programmed manually in a pseudo-hexadecimal directives. It was incredibly precise, fast as a blizzard and it definitely were games there! “The fly of a bumblebee” named one.
Further where were many programming languages studied by the manual testing, and in 2016 I push my head in to the wall. After sufficient time any piece of my programs became unreadable and seem so knotty like some aliens do it by his left toe. The same observation is True for programs written not by me. What the crap is going on!
“Why we spawn such degenerative programs?”: I asked myself and seeking for clue 4 years in deep contemplation.
And now I definitely know why it is so ugly with writing programs now. Not a secret at all. The clue to this puzzle is the nature of humans, a large set of caveats in their cognitive thinking:
Do anything and anytime like others do
If all you know about did something successfully, all what you need to do is to mimic them. In other words it is
heard style
.If it is so old - it is True by the age
A misconseption from our young childhood. Based on
All adults are genious because they're old.
You can't change it as you like
Naturally if a man following his prejudices a long time anough to do nothing at all in the shame to do something wrong.
Yes, he just can't do it. Period.
Unconsciously people do things in a some particular way and it seems a queer mistake. It is not common to find your reactive behavior as your own error looking backward.
Why you can't ask yourself: “Why it is so difficult to read any book about programming?” Easy and habitual way to take it as granted. But for me it is a usual challenge. Maybe I am personally excessively curious and persistent to digging up to core of matter. And I have to say something about programming.
Programming is easy by its nature and overcomplicated by the men. It is just something wrong with approach to study it.
As you might notice casual programming handbook often dismantle programming wrong.
Absolutely wrong. It uses English as a stem language, and
programming language itself dissected by large set of pseudo-tests -
Even if any book can't be pretentious, this one doesn't. It is exactly the essence out of learning how to program machines to execute yours and only yours orders in a most effective way of studying: by sequentially, from the elementary ones to most complex, testing its features.
Several acid notations
Defining predicatives intentionally
It's naturally to draw the boundaries around your invention. It's neither a silver bullet nor panacea for learners. Cognition demands a lot of time for reading and digesting each chapter. Nothing misterious.
Why Python
Why pure Python
Already existing solutions overview
Thurther deduction
- Conversion a boring manual into a fiction book;
- Black (Pandorra) Box approach;
- Programming languages genealogy:
- Atavisms:
- 72 char per string;
- Atavisms:
- Codex as a phenomena disclosure;
- Related material allocation on two pages at once;
- Pomodoro technique realization;
Testing as a nodal ology tool
Figure 8: An infant conducting test
If you're observed an infant behavior strictly enough you might notice that it is a silent (they can't talk yet) testing how things work. A baby with brisk ingenuity conducts the series of conscious acts and it should be interpreted as a sequence of experiments to examine the nature of events. Bear in mind its inborn origin, because every child do it absolutely autonomous, we got conclusion about a genuine and inborn human inclination on testing approach for study anything.
Chapter I: Primary concepts
Testing Python primary constants
Now it is time to concoct our first primitive set of tests to
determine the existence of Python primals True
, False
and
None
. It is intentionally simplified to show you the threshold of
primitivity in valid Python programming.
import unittest class TestLanguagePrimals(unittest.TestCase): def test_does_true_ever_exist(self): self.assertTrue(True) def test_does_false_ever_exist(self): self.assertFalse(False) def test_does_none_ever_exist(self): self.assertIsNone(None)
Yes, that's it! Try to launch this code snippet to be ensured that it works as expected:
cd tests
python3 -m unittest test_00_primal_testing_oversimplified.py
Now lets embellish our dry code with inline comments:
import unittest # <-- import header class TestLanguagePrimals(unittest.TestCase): # <-- class definition def test_does_true_ever_exist(self): # <-- an atomic test case itself # and the sample of in-line comment # self-explanatory name of the test case to focus attention self.assertTrue(True) # <-- straight assertion # If this test passed successfully than True is really exists def test_does_false_ever_exist(self): # another test case self.assertFalse(False) # False also exists def test_does_none_ever_exist(self): # the last primitive test case self.assertIsNone(None) # None also predefined by the language
Tip It is convenient to compare these two files when you open them in two panels simultaneously on the screen:
Figure 9: Two-panel editor view
You've got to catch the essence of the lesson
Now you're checked up an existence of three primary Python
constants using unittest
module and its three methods:
assertTrue
assertFalse
assertIsNone
It is a simple, minuscule and plain step onto the right path opting knowledge in its natural order.
Switching on lucid Python
Now you're ready and steady to read, compare and comprehend Python programs. Yes, there are only two of them, they are intentionally simplified, but suddenly you're got a seismic shift in your mind. From now and forever lets compose our programs in a self-explanatory manner, and Python would alleviate the pain in mulling around how the program works.
You might notice that we used the set of inline comments to describe some significant niceties. Yes, they are not obligatory though. To accomplish the switch on pure Python we should utilize a multi-line comment feature for the much broad depictions. Yes, they are not an obligation too, but in many cases they are indispensable as a source of clarity for author during writing the program and later for all its readers.
Naturally what such cool feature was already established by Python and well-known as a docstrings, the special sort of comments. Multi-line docstrings consist of a summary line, followed by a blank line, followed by a more elaborate description.
Warning, atavism!
#!/usr/bin/python
# -*- coding: utf-8 -*-
These two topmost strings in each Python program are optional and in most cases redundant.
"A universal convention supplies all of maintainability, clarity, consistency, and a foundation for good programming habits too. What it doesn't do is insist that you follow it against your will. That's Python!"
- Tim Peters
"""A module-level docstring brief single-line description A module-level docstring multi-line description. Notice the second line with a straight encoding definition. Docstrings do appear in the bytecode, so you can access this through the ``__doc__`` attribute. This is also what you'll see if you call help() on a module or any other Python object. """ import unittest # <-- import header """The place below an import section reserved for multi-line comments which might be utilized as a preamble to your Python program. It is being red only by humans, not compiled ever, and permits author to write down all stuff necessary to be at the foremost place. It might be unnecessary in our case, when we write a simplified version of programs. But our basic aim is to serve as paragon of clarity thus it should contain all lucidity's attributes. By its purpose all programming languages should allow readers to perceive what exactly this particular text do in a much explicit manner. The real state of things, when programs are habitually clogged, requires from a literate reader tons of time and perceverance for litter decluttering and a core idea grasp. Noticeable that nowadays most programs demand from its reader such literacy that it made them actually indecipherable for humans and in most cases extremely knotty even to its author. Nevertheless, this fact usually omitted by specialists, who spent a huge amount of time reading so overcomplicated texts of programs. Through time, it became a normal if anybody can't make an idea out from program's text on the fly. “It is just lack of experience!”: they say usually. You might object by notation that several professionals can't catch the essence out from that text because it is poorly written, they answer something like: “It works well enough to be scrutinized much diligently.” """ class TestLanguagePrimals(unittest.TestCase): # <-- class definition """The class-level for a brief single-line docstring Class defininition begins this reserved word /class/ following by the /name of class/ and its /parent class/ in the brackets. On first steps it might be like a magic mantra to enter the /test suite/ """ def test_does_true_ever_exist(self): # <-- an atomic test case itself """A single-line brief description for particular test case""" self.assertTrue(True) # <-- straight assertion # If this test passed successfully than True is really exists def test_does_false_ever_exist(self): # another test case """A single-line brief description for particular test case""" self.assertFalse(False) # False also exists def test_does_none_ever_exist(self): # the last primitive test case """A single-line brief description for particular test case""" self.assertIsNone(None) # None also predefined by the language """This is a conclusion multi-line comment section. It is useful to put all bottom-line conclusion there."""
Program text rectification
"""The initial test suite to check up existence of True, False and None Functionally it is the same as the programs above, but compressed for density and lucidity. """ import unittest """Now it is the moment to distill your knowledge about writing =unittests= in ~Python~. Our previous program allocates too much space in the case if you're grasp the core idea. Exactly - what is the right place and content of: - Module docstrings; - Import section; - Multi-line comments; - Test class definition; - A single test case function; - Conclusion multi-line comments are necesseties to be clear when you're in writing ~Python~ code. Lets create a much dense test suite out from existing ones """ class TestPrimariesExistence(unittest.TestCase): """Allocates all the existence tests in a single function""" def test_the_existence(self): self.assertTrue(True) # <-- straight assertion self.assertFalse(False) # False also exists self.assertIsNone(None) # None also predefined by the language """Naturally that it is more convenient to tame a dense and terse code blocks which are well-fitted on a single page. Thus, try to make up all your tests into /coherent/ blocks for clarity purpose."""
Primals comparison
"""Only code to compare None, True and False""" import unittest """If in previous tests we were introduced by three test methods only: - assertFalse(); - assertTrue(); - assertIsNone(); Right now it is a right time to show several additional ones: - assertIs() - identity assertion; - assertIsNotNone() - not None assertion; - assertNotEqual() - non equality assertion; """ class TestCompareFundamentals(unittest.TestCase): def test_none_fasle_true_comparison(self): self.assertIsNotNone(True) # Naturally, neiver False self.assertIsNotNone(False) # nor True are not None self.assertFalse(None) # !! None has a Boolean False # self.assertIs(True, True) # self.assertIs(False, False) # self.assertIs(None, None) # # self.assertNotEqual(False, True) # self.assertNotEqual(True, None) # self.assertNotEqual(False, None) # # self.assertIsNotNone(False) # self.assertIsNotNone(True) # """Seems it is much elegant than before, but less self-explanatory"""
A statement concept introduction
"""Lets meet equality and non equality operators""" import unittest """Lets immerse into statement and operators in it. The program below is identical by its meaning to previous one, but it written in a distinct manner with intention to introduce the statement and operator concepts, which are the pillars of further studying of programming.""" class TestEqualityStatements(unittest.TestCase): def test_equality_operator(self): # equality operator is double = self.assertTrue(True == True) # <-- equality statements self.assertTrue(False == False) # are inside the brackets # and they do evaluate when the program do launch self.assertTrue(None == None) def test_non_equality_operator(self): # non equality operator is != self.assertFalse(True != True) # <-- non-equality statements self.assertFalse(False != False) # are inside the brackets # and they do evaluate when you're launch the program self.assertFalse(None != None) """ You might notice that in these cases we: - assert the ~result~ of statement evaluation; - disclose the magic above only two operators """
Zero and One
"""Initial numerical values testing. """ import unittest """Lets check 0 and 1 up against already known innate Python values with already known testing methods""" class TestPrimaryNumericFeatures(unittest.TestCase): """Zero and one are pertinent innate numeric objects to test it up """ def test_zero_main_properties(self): self.assertFalse(0) # <-- numeric zero also is Boolean False self.assertIsNotNone(0) # and it is not None! self.assertEqual(0, 0) # equality assertion by the method self.assertTrue(0 == 0) # true statement assertion def test_one_main_properties(self): self.assertTrue(1) # numeric one is also Boolean True self.assertIsNotNone(1) # and it is not None! self.assertEqual(1, 1) # equality assertion by the method self.assertTrue(1 == 1) # true statement assertion def test_compare_one_and_zero(self): self.assertNotEqual(0, 1) # as expected self.assertFalse(0 == 1) # no comments self.assertTrue(0 < 1) # a new comparison operator 'less' self.assertTrue(1 > 0) # a new operator 'greater than' self.assertTrue(0 <= 1) # 'less than or equal' operator self.assertTrue(1 >= 0) # 'greater than or equal' operator """Check yourself up: Try to extend the test suite to answer questions: - Does 0 equal False? - Does True greater than False? - Does True equal 1? - etc. """