| kyle_burton ( @ 2008-06-07 09:05:00 |
| Entry tags: | perl python programming |
The same and yet different
I've just opened the book Dive into Python by Mark Pilgrim. The first example is a function that creates a (database?) connection string. Knowing Perl, I wrote a close equivalent so I could look at the differences.
First, the Python:
def buildConnectionString(params):
"""Build a connection string from a dictionary of parameters.
Returns string."""
return ";".join(["%s=%s" % (k,v) for k,v in params.items()])
if "__main__" == __name__:
print buildConnectionString({
"server":"localhost",
"database":"master",
"uid":"sa",
"pwd":"sekret"
})
Ok, defines a function of one parameter (buildConnectionString) - and that parameter is called out in the signature. This leads me to suspect that Python may actually know that the function takes a single argument (though not it's type), and that information may be available to the runtime (this is the kind of thing that helps IDEs and other software tools).
Within that function we can provide a docstring. This is something I'm familiar with from Common Lisp and it's a feature that I like. I suspect that I'll find that this is available in the Python runtime later on...
Then Mr. Pilgrim immediately indoctrinates us to Python's list comprehensions. Good. We can see that literal strings have methods (we're calling .join on the literal ";"), so they're an object (in Perl strings are not). I'm not sure what params.items() returns yet - a flat list? A set of pairs? I'm sure this is waiting for me on the next few pages.
Ok, this is a simple enough task, and the code is nice and succinct. The list comprehension makes the task clear (at least to me). What does the equivalent Perl look like? (well, my equivalent Perl, this is _my_ way of doing it, TIMTOWTDI after all).
use strict;
use warnings;
=head2 buildConnectionString
Build a connection string from a dictionary of parameters.
Returns string.
=cut
sub buildConnectionString {
my($params) = @_;
return join(";",map { "$_=$params->{$_}" } keys %$params);
}
if (__PACKAGE__ eq "main") {
print buildConnectionString({
"server" => "localhost",
"database" => "master",
"uid" => "sa",
"pwd" => "sekret"
}),"\n";
}
Ok, right off, I won't write Perl without the 'use strict;' and 'use warnings;' pragmas since they are big time savers. The first way in which they save time is that they help identify errors at compile-time that would otherwise be caught at run-time, and they point out conditions that you likely weren't handling (like using the contents of a variable before assigning to it).
Then there is the external documentation. My team has adopted the javadoc-like habit of making the api documentation in-line with the code. The difference here though is that there is no formal relationship between the POD and the function itself - unlike in the Python docstring (and in Javadoc, where adjacency is an implicit relationship). This means that the Perl documentation can't be (easily) the same kind of runtime value-add as the Python docstring (go Lisp).
The next thing is the function signature, or rather lack of, in the Perl function. Again, my team has adopted the convention of using a single line to do function argument destructuring - that way the first line of our functions is at least representationally equivalent to a signature. This makes our Perl applications a lot easier to read and maintain. Of course there are some cases (at least in Perl) when it can be useful to slightly modify the argument list, replace your function call with some other and pretend like the first never happened, though I'm not going into that right now. The main difference is that our convention isn't a formal part of the language, while in Python it is - and may additionally be a value-add at runtime (I'm sure I'll find out later, again, this is the kind of thing that helps IDEs and tools).
As far as the actual implementation, there is little logical or semantic difference between Perl's join/map and Python's join/list-comprehension. Perl's join is not a method. Neither uses an iterator off of the collection (as Ruby would), but both apply an operation to every (logical) item in a set (in the Python case it's a pair, though as I said I'm not sure how the destructuring happens yet).
There are other smallish differences too, I could have used Perl's sprintf, which would have more closely aligned with Python's implicit formatter:
sub buildConnectionString {
my($params) = @_;
return join(";",map { sprintf "%s=%s", $_, $params->{$_} } keys %$params);
}
But either language is more than capable. With the overloading of curly braces, even with my 10+ years of Perl experience, it's still a bit fuzzy to my eyes where the break is between the end of the hash-table (which is a map) and the code-block for the map. It's not that I can't read that code, or read it quickly, the dual meaning of the curly braces means that I have to use grammatical cues rather than just syntactic ones.
This is less than a first impression mind you. I'm trying hard to stay out of the Turing Tar pit. I like the list comprehensions and I like that Mr. Pilgrim introduced them right away in the book. I'm looking forward to the rest of it.