for -- else: what was the motivation?
Peter J. Holzer
hjp-python at hjp.at
Mon Oct 10 15:03:04 EDT 2022
On 2022-10-10 12:40:44 +1300, dn wrote:
> On 10/10/2022 05.56, Peter J. Holzer wrote:
> > On 2022-10-09 12:18:09 -0400, Avi Gross wrote:
> > > 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!
I knew this would be coming :-).
> 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?
They can certainly move the code around. So something like
if a:
long block a
elif b:
long block b
else:
long block c
could be compiled into (pseudo-python):
if a:
goto label_a
elif b:
goto label_b
long block c
goto end
label_a:
long block a
goto end
label_b:
long block b
end:
pass
If they can prove that it makes no difference they can also change the
order of checking conditions:
def f(a: int) -> str:
if a < 0:
return "negative"
elif a > 100:
return "big"
else:
return "meh"
could be compiled into the equivalent of:
def f(a: int) -> str:
if a > 100:
return "big"
elif a < 0:
return "negative"
else:
return "meh"
since an int cannot be simultaneously less then 0 and larger than 100.
But if the order does matter, as in
def f(a: bool, b: bool) -> int:
if a:
return 1
elif b:
return 2
else:
return 3
it can't do any transformations which would change the result.
(note that this is a point where it's really important what a language
does or does not guarantee)
>
> 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?
That depends on the CPU. Some CPUs have different jump instructions for
different sizes (for example, for a typical risc cpu the instruction and
the offset have to fit in a 32 bit word. So you would be limited to
direct jumps of plus or minus a few hundred megabytes. For longer jumps
you need to load the address into a register and jump indirectly. CISC
CPUs with variable length instructions may have finer distinctions.) A
very short jump may continue within the same cache line or maybe even on
an instruction which has already been decoded. A longer jump may have to
load the next instruction from RAM. Or a jump to the beginning of a
cache line may be faster than one to the middle. And then of course
there is branch prediction ...
So lots of variability there.
> > 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
This doesn't work. You can't continue (or break) a loop in a calling
function. (I think it works (or used to work) in Perl, but you'll get a
warning.)
You could
raise CustomContinueExcection()
and add a «try ... catch CustomContinueExcection: pass» block in the
loop.
> 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.
Yeah, there is a fine line between using exceptions to hide unnecessary
detail and using exceptions to make a program impenetrable. Or maybe
it's not a fine line but a wide grey area?
hp
--
_ | Peter J. Holzer | Story must make more sense than reality.
|_|_) | |
| | | hjp at hjp.at | -- Charles Stross, "Creative writing
__/ | http://www.hjp.at/ | challenge!"
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://mail.python.org/pipermail/python-list/attachments/20221010/e746a2e8/attachment.sig>
More information about the Python-list
mailing list