Behavior of the for-else construct

Avi Gross avigross at verizon.net
Fri Mar 4 15:52:43 EST 2022


Chris,

My example was precisely what to do when it is an empty closet:

> closet = []
>
> attic = ["Costumes", "Sheets", "Shirts" ]
>
> for item in closet:
>     print(item)
>     if item == "Shirts" : print("FOUND in closet!!")
> else:
>     for item in attic:
>         print(item)
>         if item == "Shirts" : print("FOUND in attic!!")

Of course, for a more complete program, there is a reason to break out of either loop if you find what you are looking for. I was illustrating an alternate use in what I agree is a very incomplete example as it was meant only to illustrate one point.

And my example may be implemented very differently if you need to search in dozens of places. Consider the ubiquitous PATH variable. Many programs search in one after another of the components for something like a filename. Doing that with a series of nested FOR loops with each one nested in the ELSE of the previous one might be a tad ridiculous!

I have an observation about exception handling in general. Some people use exceptions, including ones they create and throw, for a similar idea. You might for example try to use an exception if your first attempt fails that specifies trying the next attempt. In my example of the PATH variable, you might use something like calling a function and giving it what you are looking for as well as a local copy of the PATH variable content and the exception would be to call the function again with the first component of PATH removed until you fail with an empty PATH. Yes, this is similar to just having a recursive function. 

But I am hoping to get across my view that there are many ways to do things but sometimes a new feature is useful because it is more direct or other reasons. in the case we are discussing, what are the scope issues in:

eye = 1
for eye in ... :
    pass
else:
    eye = None

It looks like all references to eye refer to the same object. Some other methods we have touched on (or others may bring up) may result in different results as variables may be in other name spaces. My understanding of the PYTHON-only implementation of the above is something like this. Imagine a FOR loop that does not ignore the contents no matter what and the body looks like:

if SOMETHING_IS_BEING_DONE:
   main code processing eye sequentially
else: # only used if nothing is being done normally
   eye = None

The above made-up scenario would allow the external ELSE to be seen as the implied else to an if that tests if the loop was actually entered. Of course it is not implemented this way, but it would show why variable eye is in the same scope in the several parts. Once you play with exceptions and other functions, this may change.

The alternative suggestions about ELSE being associated confuse this issue so I want to be clear.

The main meaning of the ELSE clause does not sound quite like this. It is actually about what to do with a loop that terminates normally, as I think Chris pointed out. This example finishes the loop normally and then does the ELSE:

for x in range(6):
  print(x)
else:
  print("Finally finished!")

The following example is similar but quits early:

for x in range(6):
  if x < 4: print(x)
  else: break
else:
  print("Finally finished!")

The (second) else clause is not run.

So the example tossed at us looks a bit more like this and it does run the ELSE not because the loop is not done but because  the loop never calls a break:

for x in range(0):
  print(x)
else:
  print("Finally finished!")

So unless I am missing something, the code would destroy the last value of the variable even in a valid case when it looks like:

for aye in range(0):
  pass
else:
  aye = None

I mean if you used range(5) you would expect the value of aye==4 after the loop. The above loop would leave it as aye==None which keeps it from being undefined. To decide if the loop ran at all would thus require further code such as:

if aye == None:
    ...

Which leads me right back to wondering why the sentinel approach is so bad!







-----Original Message-----
From: Chris Angelico <rosuav at gmail.com>
To: python-list at python.org <python-list at python.org>
Sent: Fri, Mar 4, 2022 12:47 pm
Subject: Re: Behavior of the for-else construct


On Sat, 5 Mar 2022 at 03:44, Avi Gross via Python-list
<python-list at python.org> wrote:
>
> Dieter,
>
> Your use is creative albeit it is not "needed" since all it does is make sure your variable is initialized to something, specifically None.
>
> So would this not do the same thing?
>
>   eye = None
>
>   for eye in range(0):
>       print(eye)
>
>   eye
>
> If I understand it, your example depends on a variable that is not yet initialized to be used in a loop and retain the last value after the loop. You then set it to None if it is not used as the loop is skipped. Others have shown an example similar to the above of using a sentinel that lets you know if the loop is skipped.
>
> Of course, there are some advantages in making it clear by doing it you way that the loop (for example if copied and used elsewhere) needs to include the else statement as an integral part.
>
> I would like to suggest a slight modification to the above as in if you are searching for something in either seq1 and if not found in seq2. Call it looking for your green shirt in the closet and if not found, looking in the attic. Would this code make sense as such a use in several ways? In English, look here first and if there is NOTHING there, look in the second place?
>
> closet = []
>
> attic = ["Costumes", "Sheets", "Shirts" ]
>
> for item in closet:
>     print(item)
>     if item == "Shirts" : print("FOUND in closet!!")
> else:
>     for item in attic:
>         print(item)
>         if item == "Shirts" : print("FOUND in attic!!")
>
> Yes, as discussed, you could do an IF statement to check if closet is empty but for iterators, it gets ...
>

Make sure you 'break' after finding it. Otherwise, you'll keep
searching the rest of your closet, and then still go on to search your
attic. The "else:" clause doesn't help you here unless that break is
present.

ChrisA

-- 
https://mail.python.org/mailman/listinfo/python-list



More information about the Python-list mailing list