6 ms·
So is the author suggesting that a while loop like this: while not quitRequested: processNextEvent() Should be rewritten to this? for i in ra
by QuadrupleA 5y ago
So is the author suggesting that a while loop like this:
while not quitRequested:
processNextEvent()
Should be rewritten to this?
for i in range(999999):
if quitRequested:
break
processNextEvent()
Because that's supposedly guaranteed to halt? If so he's either joking or crazy. And his program will crash for his poor users after 999999 events.
- tutuca 5y agoI guess `quitRequested` falls in: > anything that runs for as long as the user says it should run; Per the article...
- QuadrupleA 5y agoWell his linter flags every occurrence, apparently. Because "that’s the fact of the matter, and you need to come to grips with it."
- detaro 5y agoIf you enable it. And if you really care about avoiding while-loops, you can still silence it for the ones you really want to have (e.e. main loops like your example, as also mentioned in the article). Probably not for me though.
- chrismorgan 5y agoIn practice, quitRequested and processNextEvent are probably both members of some managerial object; perhaps like this: while not loop.quit_requested: process(loop.next_event()) And when you see it like that, then it becomes more apparent there’s an obvious way for it to be an iterator: for event in loop.run(): process(event) This is typically harder to get wrong, and generally matches the semantics of what you’re trying to do more closely: process a stream of events. You didn’t actually care about quit_requested, you cared about the events. And so in this you can start to see why iterator-based for loops are normally better than while loops (and C-style for loops, which Python doesn’t support): while loops (and C-style for loops) are generic bookkeeping, with no specific semantics on the while condition (or for init/condition/increment clauses); but iterator-based for loops operate on actual data. This principle can be seen in resource locking also; you don’t want a lock object beside the data that it locks, like this: with lock.acquire(): queue.append(item) That style is just asking for trouble; sooner or later you’ll touch the queue without acquiring the lock, and everything will fall apart. Instead, you want to lock the actual data so that you can’t even access the locked thing without acquiring the lock: with queue.lock() as q: q.append(item) P.S. If you deal with something like the original loop without a run() method, you can write a generator to convert a while loop into a for loop: def run(loop): while not loop.quit_requested: yield loop.next_event() for event in run(loop): process(event) Most of the time the difference won’t be significant or worth it, but sometimes this can really help to clarify things, by extracting the loop bookkeeping into one place so that you can do what you were semantically trying to do, processing a stream of events. P.P.S. Instead of range(999999), use itertools.count() when you want unbounded iteration. Or itertools.repeat(None) if you don’t care about the number—but in that unused number or None see that a for loop is probably not the right tool for what you’ve written: you would be better either shifting back to while loops, or iterating over the data as I’ve demonstrated.
- jbverschoor 5y agoI usually do some sort of maximum recursion check. For things where I’d expect for example max 10 levels, I’d check for 1000-10000, and then raise an exception.
- hnfong 5y agoIn Python? Apparently the default for RecursionError is already 1000. For other languages, you might still hit stack overflow (not the site) before you get to 10000 if the stuff you push on the stack is large-ish.