found = False
for username in userList:
if username == userInput:
found = True
checkPassword(username)
break
if not found:
doSomething()
I have just learnt about the for...else... syntax, what are the benefits of using for...else... syntax? (efficiency? optimization? additional features? readability?)
The code using for...else... syntax:
for username in userList:
if username == userInput:
checkPassword(username)
break
else:
doSomething()
I am struggling whether I should change all my related code.
Short answer: No, don’t change all your code just because you have a new style available Notably, your two examples are NOT identical, so you would need to be sure that you are okay with switching to break semantics. Here’s something that IS basically equivalent:
found = False
for username in userList:
if username == userInput:
found = True
checkPassword(username)
break
if not found:
doSomething()
With this version, it’s much clearer; as long as you aren’t using found further down, it’s safe to remove the variable and replace if not found: with else:, as the semantics are the same. But this is exactly why it’s not worth making wholesale code changes just for the sake of style.
Performance isn’t really the point here. You may find that it’s a little faster (less messing around with setting and getting variables), but that’s almost never the clinching argument. Focus on expressiveness and how well either form reads. This is a case where smart people can and will disagree on what is best, and even a single person may well disagree with themselves over time (I’ve certainly changed my coding style significantly, multiple times, over my career).
You are correct, I forgot to add a break when user found in my first example. I will edit the post later.
(edit: I was promised that every element in userList is unique, so I believe that it is not a big deal in my first example?)
I should not change my codebase then.
In terms of readability, I am actually struggling thinking which one is better.
Yeah, it’s not particularly significant in this example; I just mentioned it as a reason to not go through all of your code to change it, since you have to be VERY careful of edge cases.
I like and use while-else and for-else more than some other people. Like if-else, the else part of while-else executes when the if/while condition is false, and see for loops as a specialized while loop, with the loop condition being that next(iterable) returns an item. But I would not suggest changing existing code when not otherwise refactoring.
If you already can’t decide, after being used to one way and only just having discovered the other… I guess you’ll soon prefer the new way if you get more used to it. Maybe think “nobreak” (source) everytime you type that “else”.
You are correct, I forgot to add a break when user found. I will
edit the post later.
I should not change my codebase then.
Aye.
In terms of readability, I am actually struggling thinking which one is
better.
To a degree, readability has to do with the both the overtness of the
test (eg the found=True approach) and the familiarity with the idiom
(as mentioned, plenty of us have to look up the correct meaning of for-else, myself included). But personally I’m taking that to mean
I’ve not internalised it.
One nice thing about the for-else is that if you see it, you pretty
much know that the loop goes “break on finding the target situation”
(versus plenty of loops which always run through all iterations etc).
Also, it makes the “which if the situation isn’t found” mode much more
overt.
So personally I’m trying to use it more, if only to learn it. In fact, I
used it just last night:
file = book.encryptedfiles[filename]
for userkey in lib.userkeys:
with Pfx("userkey %s", userkey):
try:
plain_contents = file.decrypt(userkey, contents)
except ValueError as e:
warning("%s", e)
continue
try:
file.check(plain_contents)
except (IndexError, ValueError) as e:
# Parse failures mean the key is probably wrong.
##warning("file.check fails: %s", e)
continue
break
else:
raise ValueError(
f'could not decrypt using any keys from lib.userkeys:{lib.userkeys!r}'
)
where it’s now clear that if we can’t decrypt contents then we’ll bail
out with an exception.
That was embarrassing. Thank you for calling me out on this.
It’s not empty-iterator checking that I was thinking of, and I was lazy and didn’t double-check what my go-to example in my homedir contains, which is…
for item in collection:
if condition(item):
break
else:
print('condition was never met')