I've noticed that a lot of the standard Python methods on lists and dictionaries don't return anything at all – they simply modify self and return nothing.
I ran into an interesting performance issue with my Google App Engine application, which I'm currently developing feverishly. It seems that if you use indexed array style access to an unfetched Query object, performance is atrocious. I'm fairly sure that it's doing a fetch for each access. It will also hit the 'DB' if you call count() on the Query object. Far superior is to explicitly fetch() from the Query to get a list, then use that. It's all fairly obvious really, but it took me a while to realise what was going on, perhaps because you can innocuously use the Query object as an iterator without this problem, which lulls you into a false sense of security.
I've stumbled across a real irritation with the way the Google App Engine data model works. One among many frankly, but I'll restrict myself to just this one for now.
- Put code after the run_in_transaction() call, that synchronises self with the data store. There isn't a sync() or refresh() method on Model objects, so you have to do this painstakingly by getting another fresh person with db.get(self.key()) and then copying across just the fields you know might have changed.
- Insist that the caller is aware that certain methods on the model objects won't modify the object itself so they need to get a fresh one. This completely wrecks the idea of an object model and encapsulation though. You'd might as well just have a purely functional interface to the data store.
I do quite a lot of futzing around developing web apps, and as a result I'm often needing to choose colours to use in them, and I want those colours in HTML hex form – e.g. #ff0000 (bright red). The standard Mac OS X colour picker is a pretty reasonable colour picker, with all the features I want: colour wheel, RGB sliders, HSB sliders etc. but it lacks the most crucial feature – easy output of HTML hex codes! This is clearly a criminal omission, and it meant I'd frequently have to convert from RGB 0-255 numbers down to hex, which is tedious in the extreme.
I've been banging away at my Google App Engine application. It'll be a little while yet, but in the meantime, a particular observation about Python. I keep getting tripped up when I refer to a member function without the parenthesis. For instance:
My MacBook laptop has had to go in for repair as a sliver is splintering off the plastic palm-rest where the lid touches it at the edge. There are also myriad hairline cracks appearing in the bottom shell around the front and back. The repair guy I took it to assured me this wasn't my fault and that it would be covered under warranty, which is good since I've got less than a month left on the year long warranty. Phew!
I've spent a bit more time learning Python, because I've been dabbling with Google App Engine (which I'm going off rapidly) but mainly because it's an interesting language. What's particularly intriguing to me is its similarity to Ruby, and hence where it differs in syntax or approach makes for a notable point of comparison. Of course it's probably more correct to say that Ruby is similar to Python than the other way around. From my still very small exposure to Python…
- Less confusion with the way classes work. Or maybe I just haven't stumbled into Python's equivalent of meta-classes and class vs instance variables.
- @classmethod is a much neater way of stating what's a class method rather than an instance method, compared to all that self gubbins, or the dreaded <<.
- Optional named arguments for functions, allowing more flexibility for optional arguments and greater clarity when calling. [As an aside I like Objective-C's way of building argument labels into the method signature, but not it's square brackety syntax: [obj message:foo]. I'd much rather do obj.message(foo) and in fact with properties in Objective-C 2.0 we see more of this style.]
- I think I probably prefer explicit return statements rather than the Ruby way of returning the last evaluated thing in any expression.
- List comprehensions. To start with it just seems like a syntactic difference – Python: [x*2 for x in my_list] Ruby: my_list.map {|x| x*2}. But Python's party trick is excluding elements as it goes: [x*2 for x in my_list if x != 3].
Nasty things about Python:
- Seriously – indentation to demarcate blocks? Apparently I'll get used to it.
- Double underscores. __init__ is a complete pain to type. Why not just a single underscore at the start or something?
- Fiddly module system. Why the need for an __init__.py file in a directory, just to make it a module? Why the need to explicitly define an __all__ method just to be able to import everything in a module? I want to be able to create a "model/" directory, put all the .py files for my DB classes in there, then import the whole lot from my other classes with ease. When I add a new model class, I shouldn't have to go and modify the __init__.py file. It seems to be a real pain to split code up into multiple files sensibly in Python. If I've missed a trick here – please somebody show me the light!
- The string interpolation is OK I suppose, giving the full power of C style formatting, but most of the time you're just doing simple interpolation and Ruby's syntax is far more pleasant and readable. Python: "Hello %s, from %s." % (person, greeter) Ruby: "Hello {person}, from {greeter}." Ruby also has a full on formatting system for the few times when you need it.
Having ploughed through much of the documentation, done the tutorial and started writing my own little web app, I have some half-formed thoughts about Google App Engine to throw out to the world.
- As far as I can tell, any sort of data aggregation functionality (counting, averaging etc.) just won't be possible as the Datastore APIs don't allow for it. I've tried to think of ways to fake it but even my most elaborate machinations come up against the buffers. The only way to manage it at all is to do counting and averaging piecemeal, manually keeping the aggregate values you need up to date with each individual entity modification. Unfortunately, that means that you can't introduce new functionality requiring new aggregate values after you've already got a million users, because you've missed the chance to record those aggregates along the way.
- Python's OK, but I don't like using indentation as the sole way to define blocks. But I'm sure I can get used to that.
- I really don't like having to put an empty __init__.py file in any subdirectories of my python code. If I don't do that it seems I can't import foo.bar.Thingy. Breaking up code into multiple files in a sensible directory structure is surely a fairly common thing to do, so I'm amazed that Python makes it strangely difficult. I hope I've simply missed something and it's actually easier than that.
- In fact all those double underscores look horrid and are a pain to type. Surely a single underscore would have been quite adequate?
- The overall experience for learning GAE is very sorted. Smooth and well integrated – all you need to supply is your own decent text editor. I'm trying out TextMate, the darling of Mac OS X code editors, but I'm worried to see that it doesn't seem to have been updated for over a year.