[Tutor] Why Doesn't the for loop Increment?

dn pytutor at danceswithmice.info
Fri Jan 15 19:48:30 EST 2021


On 16/01/2021 02.24, Stephen P. Molnar wrote:
...

> Now I'm really tearing out what little hair I have left - were did the
> '\n' originate??????
> 
> Sorry to be such a dunce (must be a hangover from FORTRAN II).


Exactly!
(the FORTRAN bit, not the "dunce")


Traditionally languages, and certainly FORTRAN-II/-IV, accepted input as
fixed-length records, ie each read/input instruction expected the same
number of characters. (think of punched-cards and their 80-characters -
whether all 80 were utilised, or not)

Whereas Python (typically) uses "streams". That is to say, a file is
thought-of as a stream of characters (cf a series of records).

In this format, the first 'record' may contain two characters and the
next twenty. (stream-files do not use the term "record", but we are used
to it, so will continue to do so - if only for the length of this
conversation!)

However, the idea of a "stream" (of characters) raises the question: how
are two consecutive 'records' separated/how do we know where each begins
and ends? Thus, the term: "separator".

NB Sadly, the particular separator used in particular situations may not
be the same on all machines/Operating Systems. eg in pathnames
MS-Windows uses "\" whereas the rest of the world uses "/"; and although
some use a newline 'character' to indicate the ends of 'records' in
files, others use 'carriage return', or indeed a combination of
'carriage return, line-feed'. Sigh!

You will find this discussed in the Python documentation, eg os library,
read() and input(), etc - important to research because some include the
separator and some "strip" it from 'the data', and Python's input() may
'behave' differently from some other library's input() 'equivalent
function'!
(thus @Alan's suggestion to use str.strip() to remove those pesky
'superfluous' "\n" ("New line") characters!)


Although not part of your question, in the case of print(), there is a
"sep" parameter and another for "end". The latter enabling override of
the system-default. Now you can see why! (if not, you will next...)


The former ("sep") brings us neatly to the concepts of CSV-files (comma
separated variables), whose very name features the word "separated", if
not "separator" (the character/symbol which achieves the "separation").
The "comma" separates one field within a record from its successor. You
may have come-across a variant which uses the tab-key/-character instead
(technically/precisely called a TSV-file). Both names ignoring the point
made above, that there is *also* a need to define a separator to
separate 'records'!

Before long, such flexibility induces one to part-quote Sir Walter Scott
(English-English literature) "oh what a tangled web we weave"!
(apologies, I've been having way to much fun with word-games this
morning...)


You can see the value of earlier contributor's advice: that
input/processed values be carefully-checked to ensure that they are
exactly what is required - no more, no less! Remember the old adage:
"GIGO" (garbage in, garbage out)!
(and complementing the recommended research, above)


As a further update/improvement to your coding: Have you come-across TDD
(Test-driven Development)? If not, with a brief investigation, it will
likely recommend itself, in part because of its parallels with 'the
Scientific Method', ie hypothesise then (dis)prove (+).

Thus, in logic we might say: if we have a and b as inputs, function-f
will produce output-c - alternately: if we need output-c from inputs-a
and -b, function-f must transform by ...

In Python-code, we can say:

    assert f( a, b ) == c

If execution of f() works correctly, such a test is deemed 'successful'
because the assert will yield True, and execution continues. If the
function produces a result other than the (expected) value-c, then the
assert[ion] yields False, and we conclude that there is either something
wrong with the data, or with the function!

So, from such a basis TDD says: write the test/assertion first. (*).
Then write f()'s code. Then run the test (cf running the function
directly), to 'prove' the code. If the test fails, consider the error -
which one assumes will require a code-change/correction. 'Rinse and
repeat', until the test 'passes'.

(* some put a 'test run' at this point, but without any function-f it
must surely fail/the result is a foregone conclusion, so this lazy-boy
wonders: why bother?)

Note that these tests which one writes should be kept separate from what
will eventually be the delivered 'production code'. However, they are
not one-off routines to be discarded upon delivery! Should the code be
augmented in future, the tests will be very useful to ensure that these
'new changes' don't 'damage' the existing code/fail to meet the original
objectives!

These are termed "unit tests", because we decompose the
overall-problem/hypothesis, into smaller problems, until we arrive at an
easily solvable/soluble, smaller, unit of code. If we test each unit,
then we have the basis of an expectation that the entirety will work.
More importantly, we are some way towards a "proof".

Obviously, you will want more than one test, both for a single unit and
because the whole-program(me) consists of multiple "units"; so a 'test
runner' eg PyTest; is a very handy tool and can usually be employed as
an add-in to one's IDE (apologies: I can't comment on Spyder).
Test-runners have the advantage of running every test, even if one
'fails' (which an assert statement, on its own, will not). The IDE can
usually be set to run the tests every time you save the file, and
CVS/DevOps systems (should you use such) can require the test-suite be
run before any code is delivered to users...


In summary, using Python after FORTRAN looks much the same, yet offers
many exciting 'new'/extra opportunities - but it also requires us
'silver surfers' to update some of the philosophies and "semantics"
within our coding practice and that are particular/pertinent to this
programming language.

Accordingly, in addition to the previously-mentioned Python
research/reading/learning, may I recommend these two higher-thought
ideas to you:
1 stepwise decomposition/modular programming - to assist your
'translation' of real-world needs into (Python) code.
2 TDD - to identify short-comings in the solution (and/or your
understanding of Python) at the lowest-possible level/earliest-possible
juncture - ensuring that each module of code contributes correctly
towards the wider, desired solution.
-- 
Regards =dn


More information about the Tutor mailing list