"also" to balance "else" ?

R

Ron Adam

Andrew said:
As someone else pointed out, that problem could be resolved in
some Python variant by using a different name, like "at end".
Too late for anything before P3K.

It was pointed out to me the logic of the else is consistant with the if
in reguard to the loop test it self, it's just when you apply it to the
loop as a whole, the resulting expected code flow becomes counter
intuitive. So it's isn't so much of a problem as it is a gotcha, or
something that has the potential to be a gotcha.

I was thinking that changing the else to an also, would be a small
change, but because I miss understood just what it was testing for. It
would not be correct to change it.

Maybe just removing it from python 3k would be the better choice?

Adding a seperate loop enter/exit test to determine if a loop was exited
with break after it has started, would be a seperate issue and one that
would need more support than the smaller change I was thinking it would
require.

Let me clarify. When is it useful in real code? Most cases
I can think of have corner cases which treat some paths different
than others.

Here are some examples that I've found from the libary where the program
flow matches the if-elif-also logic but was writen differntly.


#from urllib.py
def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
if 'location' in headers:
newurl = headers['location']
elif 'uri' in headers:
newurl = headers['uri']
else:
return
void = fp.read()
fp.close()
# In case the server sent a relative URL, join with original:
newurl = basejoin(self.type + ":" + url, newurl)
return self.open(newurl)


#could be...
def redirect_internal(self, url, fp, errcode, errmsg, headers, data):
if 'location' in headers:
newurl = headers['location']
elif 'uri' in headers:
newurl = headers['uri']
also:
void = fp.read()
fp.close()
# In case the server sent a relative URL,
# join with original:
newurl = basejoin(self.type + ":" + url, newurl)
return self.open(newurl)



#from difflib.py
tag = ''
if i < ai and j < bj:
tag = 'replace'
elif i < ai:
tag = 'delete'
elif j < bj:
tag = 'insert'
if tag:
answer.append( (tag, i, ai, j, bj) )

#could be...
if i < ai and j < bj:
tag = 'replace'
elif i < ai:
tag = 'delete'
elif j < bj:
tag = 'insert'
also:
answer.append( (tag, i, ai, j, bj) )

This one is simular to the code I was writing when I want'ed to use an
inverse else. It doesn't require 'tag' to be initialized before hand.


# from imputil.py
class BuiltinImporter(Importer):
def get_code(self, parent, modname, fqname):
if parent:
# these modules definitely do not occur within a package
context
return None

# look for the module
if imp.is_builtin(modname):
type = imp.C_BUILTIN
elif imp.is_frozen(modname):
type = imp.PY_FROZEN
else:
# not found
return None

# got it. now load and return it.
module = imp.load_module(modname, None, modname, ('', '', type))
return 0, module, { }

#could be...
class BuiltinImporter(Importer):
def get_code(self, parent, modname, fqname):
if parent:
# these modules definitely do not occur
# within a package context
return None

# look for the module
if imp.is_builtin(modname):
type = imp.C_BUILTIN
elif imp.is_frozen(modname):
type = imp.PY_FROZEN
also:
# got it. now load and return it.
module = imp.load_module(modname, None, modname, \
('', '', type))
return 0, module, { }

# not found
# return None


None of these are big or dramatic changes of the sort that I couldn't
live without. Finding examples in the library is more difficult than I
expected, so while this seems like a good idea... I'm not convinced yet
either. But that is why I posted is to find out what other thought.

Regurds, Ron
 
F

Fredrik Lundh

Ron said:
So the (my) confusion comes from the tendency to look at it in terms of
overall program flow rather than in terms of the specific conditional
logic.

In a for loop the normal, as in terminating normally, behavior of a loop
is one where the loop test evaluates as 'False' ending the loop. And
the abnormal or counter behavior is when a break statement executes.
Thus the 'else' block is the normal result, and the skipping the 'else'
block becomes the abnormal counter behavior.

a typical use-case for for-in-else is a search loop:

for item in collection:
if predicate(item):
print "found", item
break
else:
print "not found"
return

print "use", item

where your "abnormal behaviour" is, of course, the expected
behaviour. if you insist on looking at things the wrong way,
things will look reversed.

</F>
 
R

Ron Adam

Fredrik said:
Ron Adam wrote:




a typical use-case for for-in-else is a search loop:

for item in collection:
if predicate(item):
print "found", item
break
else:
print "not found"
return

print "use", item

where your "abnormal behaviour" is, of course, the expected
behaviour. if you insist on looking at things the wrong way,
things will look reversed.

</F>

It isn't so much as reading it the wrong way, as it is to how it reads
in the context it is used in. In the above it works out that the
negative search result is a successful loop completion, so the else
reads correctly in that context.

If the context was one of verifying completeness or correctness of a
collection, then the context would be reversed and the else would
indicate success instead of failure.

I do see it both ways. Testing for an item in a list is probably the
more common occurrence.

If anything, this just points out the error of trying to apply code
logic to a larger context. They aren't always the same. It is nice when
code logic matches the context it's used in, as it makes the code much
easier to read and understand.

My initial thought of adding 'also' was that it might lead to being able
to write more code where the code logic more directly parallels the
context it's used in. From the few example uses I've found in the
library, it looks like it wouldn't make that big of a difference.

Maybe someone else can find more uses than I have. <shrug> I tried.

Regards, Ron
 
T

Terry Hancock

where your "abnormal behaviour" is, of course, the expected
behaviour. if you insist on looking at things the wrong way,
things will look reversed.

Unfortunately, the converse is true, too: no matter how twisted
an idea is, you can make it seem logical with the right point
of view. ;-)

I think the OP is correct in saying that for-else is non-intuitive.

Your use case is the only one in which "else" makes sense, IMHO,
and that's only because it reflects the overall meaning of the
algorithm, not because it actually reflects what's going on in
the syntax.

If "finally" is wrong (and I see your point there), then maybe
"normally" is the right thing to call it? After all, it's the opposite
of the "exception" case, which is probably the "normal" case.

The fact that, in a search algorithm, the exception case is the
normal result, and the syntactically normal case is the exception
just confuses the issue. The question is, how difficult is it for
the person reading the code to understand what was written?

Now of course, a person skilled in language X will understand
the syntax even if all the keywords are unintelligible
gibberish, and from that PoV, it will surely make sense. But
I get the impression that Python is *trying* to use words with
the right inuitive meaning in the interest of helping newbies.

I *personally* can get my head around "for-else" and use it
if I really need it, but I agree that it could be spelled better.

OTOH, I see no reason for an opposite construct, since, as you
and others have pointed out, that can be handled by the if
in the loop or by an exception handler.
 
N

Nicolas Fleury

Ron said:
It occurred to me (a few weeks ago while trying to find the best way to
form a if-elif-else block, that on a very general level, an 'also'
statement might be useful. So I was wondering what others would think
of it.

But the feature is already there:

for x in <iterable>:
BLOCK1
if <condition>:
ALSO-BLOCK
break
else:
BLOCK2

If find "else" fine, since the only times I used it is in searches:

for x in <iterable>
BLOCK1
if <gotcha-condition>: break
else:
raise Exception('Not found')

In that case, 'else' sounds like the good keyword.

Regards,
Nicolas
 
R

Ron Adam

Terry said:
Unfortunately, the converse is true, too: no matter how twisted
an idea is, you can make it seem logical with the right point
of view. ;-)

I think the OP is correct in saying that for-else is non-intuitive.

Although it took me a while to verbalize it, My motivation for
suggesting 'also' was/is that it might enable writing code that better
matches the context it's written in. Code that's more intuitive and as
a result more readable.

There are 4 possible outcomes to a loop.

1. Not Entered. Nothing done. (current possible for-else behavior)

2. Entered. Some loops may have completed.

3. Not finished. (break) Some loops may have completed.

4. Finished (no break) All possible loops completed.
(current for-else behavior)

(This doesn't include exit by return statements.)


One possibility may be to use a keyword that parallels the except in
try-except blocks which will accept a test designator. (building on your
refernce to finally)

(This is probably a definite after 3k item I think, or more likely just
academic discussion, but I enjoy that too.)

So use loop as the new keyword with the above conditions and have the
else as the alternative to any of them.

(Feel free to suggest different words)


for item in collection:
if <condition>:
print "item found"
break
loop Finished:
print "item was not found."
return


for item in collection:
if <condition>:
print "bad item"
break
loop Finished:
print "The Collection pass's the test"
else:
print "The Collection is invalid"


for item in collection:
<do something>
loop NotEntred:
print "The Collection was empty"


for line in textbuffer:
print line
loop Entered:
print time.date()
else:
print "The buffer was empty"


while <condition>:
<do something>
if <condition>:
break
loop Entered:
<do something>


I think these are sufficiently explicit as to avoid being non-intuitive.
The endloop might be generalized into a endblock or endsuite statement
possibly. I'm not sure if that would have any uses's in the proposed
"with" statements or not. (?)

Regards,
Ron
 
R

Ron Adam

Nicolas said:
But the feature is already there:

Yes, that was pointed out to me, I was first interested in it as an
if-also, which I found a few uses for and thought that it might
naturally be extended to for and while, but the interpretation of the
else in for loops conflicts with it, so it's ruled out in that form.
See the last message I posted in this thread for a more explicit
alternative that is more flexible and have less of a tenancy to be
counter intuitive. Although a bigger change overall.

I don't expect too much support for that one either, but who knows. The
conversation is interesting (to me). ;-)

Cheers,
Ron
 
T

Tim Roberts

Nicolas Fleury said:
But the feature is already there:

for x in <iterable>:
BLOCK1
if <condition>:
ALSO-BLOCK
break
else:
BLOCK2

I've been using Python for 8 years. I never knew that feature was in
there.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,954
Messages
2,570,116
Members
46,704
Latest member
BernadineF

Latest Threads

Top