1

I ran the this block of code and get errors like below:

Traceback (most recent call last):
  File "urllister.py", line 26, in <module>
    for k in l: print k,"points to",l[k],"\n"
RuntimeError: dictionary changed size during iteration

The only thing I do is printing in the for loop at line 27

from sgmllib import SGMLParser

class URLLister(SGMLParser):
    def reset(self):
        SGMLParser.reset(self)
        self.data = []
    def start_a(self, attrs):
        href = [v for k , v in attrs if k == 'href']
        if href:
            self.data.extend(href)

if __name__ == '__main__':
    import urllib
    sock = urllib.urlopen("http://diveintopython.org")

    parser = URLLister()
    html = sock.read()

    parser.feed(html)

    sock.close()
    parser.close()
    for url in parser.data: print url
    l = locals()

    for k in l:
        print k,"points to",l[k],"\n"
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
pythoniku
  • 3,532
  • 6
  • 23
  • 30

3 Answers3

10

The reason for this is that you loop introduces a new local variable, k, which means that your dictionary of local variables is changed in the loop.

The easiest solution (if you really need to do this, although it's a sign of a bad idea, generally) is to copy the dictionary - e.g: l = dict(locals()). This way the original being updated won't cause problems.

Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
  • you are right about introducing a new variable k, but in the book "diveinto python" http://www.diveintopython.net/html_processing/locals_and_globals.html locals() already returns a copy of that dictionary – pythoniku Jan 03 '13 at 17:36
  • 1
    According to python doc http://docs.python.org/2/library/functions.html#locals, that's not a copy for python 2.7.3. Lattyware is right. Diveintopython might not be up-to-date – trez Jan 03 '13 at 17:46
  • 1
    @cjk: the book used an unfortunate choice of words. The author was trying to explain the reason that modifying `locals()` doesn't change the variable -- but actually, it can sometimes (in CPython if you don't do it from within a function, for example). If you forget about the fact that sometimes it does work, calling the result of `locals()` a copy and `globals()` not a copy gives you the rough idea. But `a = locals()` `b = locals()` `print a is b` gives `True`; `locals()` does not return a copy. – DSM Jan 03 '13 at 17:52
  • @cjk you may want to look at [this question](http://stackoverflow.com/questions/7969949/whats-the-difference-between-globals-locals-and-vars) for more examples of how to use (or abuse) locals(). – Don Spaulding Jan 03 '13 at 17:53
0

The reason for the error is that Python thinks, since you access the dict by key, you could change the dict, which is restricted here. To avoid this error, you can use get method and your statement will look like this then:

for k in l:
    print k,"points to",l.get(k),"\n"
nonsleepr
  • 801
  • 9
  • 12
0

The reason for this is that you loop introduces a new local variable, k, which means that your dictionary of local variables is changed in the loop.

The easiest solution is to define k before the loop so that locals() doesn't change once the loop has started.

k = 0
l = locals()

for k in l:
    print k,"points to",l[k],"\n"
duanev
  • 936
  • 13
  • 17