Default arguments in Python: two easy blunders
I’m glad I stumbled across Patrick Altman’s tweet about a “default bug in Django“. I’d never have guessed you can pass a callable to a field’s default= argument, otherwise. That’s quite a powerful idiom, and I think I’ll use it a lot.
To balance the karma, I’d like to post a quick reminder to everyone else that expressions in default arguments are calculated when the function is defined, not when it’s called. In Patrick’s code, for example, all objects created in the same running session got the same timestamp. Try this in the Python interactive prompt:
>>>import time >>> def report(when=time.time()): … print when … >>> report() 1210294387.19 >>> time.sleep(5) >>> report() 1210294387.19
Until the interpreter quits, you’ll always get the same timestamp. The correct way to go about this is to default to None or some other sentinel, then replace it inside the function:
>>> def report(when=None): … if when is None: … when = time.time() … print when … >>> report() 1210294762.29 >>> time.sleep(5) >>> report() 1210294772.23
Now that you know about that blunder, you should be able to figure out what’s going on with this second classic blunder when using default arguments in Python:
>>> def spam(eggs=[]): … eggs.append(”spam”) … return eggs … >>> spam() ['spam'] >>> spam() ['spam', 'spam'] >>> spam() ['spam', 'spam', 'spam'] >>> spam() ['spam', 'spam', 'spam', 'spam']
Twitter Emergency Backup
Dave Winer has been thinking about ways to preserve the Twitter community even when Twitter is down. His latest effort is a web service to save Twitter feeds and expose their content via another feed.
I tried it out, but couldn’t parse the response:
Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17) [GCC 4.0.1 (Apple Inc. build 5465)] on darwin Type “help”, “copyright”, “credits” or “license” for more information. >>> import xmlrpclib, urllib2 >>> twittergram = xmlrpclib.Server(”http://rpc.twittergram.com/RPC2/”) >>> content = urllib2.urlopen(”http://twitter.com/statuses/user_timeline/704593.rss”).read() >>> twittergram.saveFeed(’garthk’, ‘*****’, content) Traceback (most recent call last): File ““, line 1, in File “/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/xmlrpclib.py”, line 1147, in __call__ return self.__send(self.__name, args) File “/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/xmlrpclib.py”, line 1437, in __request verbose=self.__verbose File “/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/xmlrpclib.py”, line 1201, in request return self._parse_response(h.getfile(), sock) File “/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/xmlrpclib.py”, line 1335, in _parse_response p.feed(response) File “/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/xmlrpclib.py”, line 547, in feed self._parser.Parse(data, 0) xml.parsers.expat.ExpatError: mismatched tag: line 10, column 7
I’ll investigate later.
I’d love to wade into this kind of problem domain, perhaps using Google App Engine for hosting so I didn’t have to worry about usage spikes. Feed backup is a brilliant start. We also need to helping our followers find the feeds, and I’m sure more thought will yield more areas for funimprovement.
Twitter’s Famous Last Words?
Just before Twitter went completely dark, I saw this:

I’m sure it’ll be back up soon, but ’twas nice to have a laugh before hitting the sack.
(Speaking of irony: I have 666 unread items in my feed reader’s Productivity folder…)