4 ms·
The other scoping issue in python that always struck me as strange is that loop variables aren't scoped to the loop, they continue to exist after the loop compl
by mbell 12y ago
The other scoping issue in python that always struck me as strange is that loop variables aren't scoped to the loop, they continue to exist after the loop completes. I can see the logic for this feature even if I don't agree with it, but what I really don't get is that the loop variables are not defined if you iterate over something that is empty:
>>> for item in [1]:
... print item
1
>>> item
1
>>> for i in []:
... print i
>>> i
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'i' is not defined
I would expect i == None. That oddity makes it dangerous to use the feature unless you're really careful (e.g. using a for - else construct).
- pyre 12y agoThen there's for-else: In [1]: for i in []: ...: pass ...: else: ...: print 'Else!' ...: Else! In [2]: for i in []: ...: break ...: else: ...: print 'Else!' ...: Else! In [3]: for i in range(2): ...: break ...: else: ...: print 'Else!' ...: In [4]: for i in range(2): ...: pass ...: else: ...: print 'Else!' ...: Else! The syntax could be interpreted as: if len(l) == 0: print "Else!" else: for i in l: pass The "catch cases where a `break` is triggered" case isn't common enough for this syntax feature to be encountered very often, leading to confusion when people come across it (though at least it's not a bug where a common use-case has weird behavior to new-comers).
- dragonwriter 12y ago> but what I really don't get is that the loop variables are not defined if you iterate over something that is empty if you conceptualize how a for-loop has to work as a while-loop using Python's iterator protocol (which is the only way the iterator protocol itself makes sense), it seems pretty intuitive. That is, this: for item in items: ...1 else: ...2 becomes, approximately: try: while True: __hidden_iter = items.iter() try: item = __hidden_iter.next() except StopIteration: raise __NormalLoopExit ...1 except __NormalLoopExit: ...2 If you have an empty loop, the first assignment doesn't complete (instead raising StopIteration in evaluating the right side, which raises the notional exception __NormalLoopExit, which invokes the else: clause, if any) so the variable never gets around to being created.