On Sun, 9 Oct 2022 at 15:39, Axy via Python-list
<python-list@python.org> wrote:
"shortest block first"
Have never heard this advice before. Kind-of rankled with me, as it did
for others.
Enquiring minds want to know... Played Duck, duck, go on this: zero hits
amongst a pile of similar phrases - turns-out there's an algorithm with
a similar name, but not related, and an electronics approach (way too
'low' a level for translation to us though).
Tried prefixing with "program" but no such advice to programmers or
program[me] designers.
Tried prefixing with "python", but equal lack of joy.
Would OP please quote source?
On 10/10/2022 05.56, Peter J. Holzer wrote:
On 2022-10-09 12:18:09 -0400, Avi Gross wrote:
Smallest code blocks first may be a more modern invention.
None of the recent-grads or new-hires I've asked this morning (it's
already Monday over-here!) have used or heard the term.
Some would argue for a rule related to efficiency of execution. When you
have multiple blocks as in an if-else or case statement with multiple
choices, that you order the most common cases first. Those shorten
execution more often than the rarer cases especially the ones that should
never happen.
Those of us who started programming on 8 bit homecomputers of course
have efficiency always at the back of their heads, but I find this
... for mainframes just as much as micro-computers!
Regarding execution-efficiencies, I'm sure @Avi knows better than I: It
seems likely that Python, as an interpreted language, will create
'blocks' of its virtual-machine code in the same order as they appear in
the Python-source. However, aren't there optimising compilers which do
something intelligent with the equivalent clauses/suites in other languages?
Regardless, is a Jump-instruction which transfers else-control to a
block five machine-instructions 'down', any less efficient than a jump
which spans 50-instructions?
So not a rule but realistically not always a bad idea to write code in a
way that draws the attention of readers along the main path of execution
and perhaps not showing all the checking for odd cases first.
much more important. Putting the main path first makes it easier to
understand what the code is supposed to do normally. All those pesky
exceptions are in the "small print" below.
Absolutely! Has the term "readability" been used 'here'?
Human nature (or is it that of computer programmers in-particular) is to
be optimistic: it will work [this time*]. Accordingly, a colleague talks
of always coding 'the happy line' first (meaning line of logic, cf
source-code).
Contrarily, for while-True (infinite) loops, and particularly recursive
algorithms, the [wise] counsel is to code the end-condition first.
(always know your closest exit! "The nearest exit may be behind you"...)
Indeed, dare I say, this optimistic-approach is pythonic. Taking an
over-simple, two-value division example, the approach is:
try:
a = b / c
except ZeroDivisionError:
... clean-up the mess ...
which contrasts the EAFP philosophy of Python versus the LBYL
expectation of (many) other languages:
assert c != 0
a = b / c
That said, as "Data Science" use of Python expands, it is bringing more
and more needs for an LBYL attitude, eg "data cleaning".
(EAFP, LBYL? https://docs.python.org/3.9/glossary.html)
There is of course the opposite view that you should just get all of the
confounding factors out of the way first, so that the default is also
the common case. I also do that sometimes, but then I don't hide this in
in an else: clause but do something like this:
for item in whatever:
if not_this_one(item):
continue
if neither_this_one(item):
continue
if cant_continue(item):
break
if oopsie():
raise SomeError()
do_something_with(item)
and_some_more(item)
we_are_done(item)
which shows visually what the main purpose of the loop (or function or
other block) is.
Nicely stated!
NB I've seen sufficient of @Peter's posts to know that this was never
(even implied to be) intended as a snippet for all occasions!
It also illustrates why such is less readable: because we have to scan
four if-statements before we can 'see' the purpose of the loop. My
'itch' would be to extract this code 'out' to a function - that way the
name will convey the somewhat-obscured purpose of the loop.
Alternately, reduce the 'distractions':-
try:
for item in whatever:
inspect_the_data( item )
do_something_with(item)
and_some_more(item)
we_are_done(item)
except SomeError:
...
except CustomBreakException:
... ?pass? # same effect as break
by 'hiding' in:
def inspect_the_data( item ):
if not_this_one(item):
continue
if neither_this_one(item):
continue
if cant_continue(item):
raise CustomBreakException # was break
if oopsie():
raise SomeError()
It is now easier to understand 'the happy line', ie the thinking of the
original-coder, and the 'small print' has been relegated to such and can
be cheerfully disregarded.
Whereas, if 'exceptional circumstances' is the reason one is inspecting
the code in the first-place, then it also helps to have separated-out
the ifs-buts-and-maybes, and into a structure which can be as closely
(and exhaustively) tested, as may be required.
In some ways, (IMHO) there are reasons to feel disquiet over this style
of coding. Learning "Modular Programming", and slightly-later
"Structured Programming", when they were still new (?fresh, ?exciting),
we were inculcated with the 'one way in, one way out'
philosophy-of-correctness. This applied to "blocks" of code (per
"module"), as well as formal units, eg functions.
Accordingly, am slightly unnerved by seeing Exceptions being used to
'jump out' of interior/nested blocks, rather than using the
return-mechanism (taking their turn like all the good little boys and
girls). That said, it makes for tidier code - so I'll stop muttering
into my (grey) beard ...
The alternative, assuming the 'errors and omissions' function is a
possible tactic(!), would be to return a boolean, eg
def is_clean_data( item )->bool:
is_verified = False
if ...
...
return is_verified
- thus the do-stuff calls will become a 'successful' if-then 'suite'.
There is more code to write/read - and the toy-example lends itself to
such a tactic. In other situations, perhaps some refactoring or
pre-processing, even a decorator, might remove (or reduce) the need for
so much checking within the loop/block.
When to use one or the other approach?
We could hide behind some 'mystery', and say "I just know from
experience", but that smacks of a secret coin-toss (human 'gut feelings'
not being particularly indicative of success). So, here's a stab at it
which harks back to the learn/use 'COBOL or FORTRAN' [argument] days:
If the purpose/considerations of the for-loop (block), are:-
- data-related: check-first
(and thus consider extract and/or invert to promote readability)
- logic/algorithmic implementation, take 'the happy line' first
(and deal with the exceptions ("small print") later)
Worthy of consideration, is that Python is (still) fast-developing. The
switch-case construct of v3.10, and protocols and beefed-up descriptors
(?interfaces?) could have quite an impact on such thinking, and in the
relatively near-future...
* back in the ?bad old days when testing was something that was (only)
done AFTER coding was complete, ie ego-driven development. The
traditional response to the question: "are you coming to
lunch/dinner/supper/break/the party/bed/...?" was "in a moment -
[surely?] there's only one more bug"!
I've been 'dipping into' Martin Fowler's "Refactoring", 2e, Pearson,
2019; but don't have it with me to point to useful references. What I do
have to-hand, because it has just arrived, is Mariano Anaya's "Clean
Code in Python", (also 2e), Packt, 2020* - although I didn't see its
previous edition, and have read nothing beyond the Contents(!) to-date;
it talks of "Design by Contract", "Defensive Programming", "Separation
of Concerns" indicating it may have thinking to offer.
* the reviewer was Tarek Ziadé, author of "Expert Python", which is
worth reading - as are his other works (in English and in French)
--
Regards,
=dn
--
https://mail.python.org/mailman/listinfo/python-list