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