Python in 1999 and now from a Perl perspective

I found
an old post
by Tom Christiansen, of Perl fame, to the Python mailing
list were he posted in detail his observations of Python. 7 years
have gone by, and since then Python has evolved and I think it might
be interesting to take his points and see if they are still valid. Be
warned that this is a VERY long post.

Per your collective requests, here is one of the documents
with my collective observations from learning Python.

This article was posted to the Perl newsgroup, but I have duplicated
the posting here without crossposting to avoid flame wars.

–tom

SIMILARITY:
When Python talks about tuples, lists, and dictionaries,
Perl people should think of lists, arrays, and hashes
respectively. Except that tuples in python are 1st-class
citizens. (Unclear whether this is good; see below.)

I don’t have anything to say to that.

COOLNESS:
Python’s print() function is more like the Perl debugger’s
“x” command; that is, it’s recursive and pretty-printed.

Another cool thing about Python’s print statement (it’s not a function yet, that’s coming in Python 3000 though) is that it adds a newline automatically, something I sometimes forget in Perl.

GOTCHA: (medium)
The whole mutability versus immutability thing causes many
confusions. For example:
t = ([1,2],3)
t[0][0] = “fred” # legal
t[1] = “fred” # ILLEGAL
I don’t understand why tuples weren’t just lists that weren’t
somehow marked “read-only” or “constant”. That way you
could do the same with dictionaries or any other data types.

I’m not sure I ever understood the point of tuples either.

GOTCHA: (low)
You can’t use “in” on dicts. Instead, you must use
d = { “fred”:”wilma”, “barney”:”betty” }
if d.has_key(“fred”): # legal
if “fred” in d: # ILLEGAL
I don’t understand why this was done.

This has been fixed:

  >>> d = { "fred":"wilma", "barney":"betty" }
  >>> "fred" in d
  True

SIMILARITY:
Both “and” and “or” return their last evaluated value
in both Python and Perl.

Nothing to say there.

GOTCHA: (low)
I don’t understand why we have so many C operators, such as “&” and
“|” and “~”, but we don’t have “&&” or “||” or “!” We also have “< >”. It’s bizarre that the relational and “?:” are missing.
Python uses so much from C to make people see what they expect to see,
but this is missing. Why?

I am of the opinion that && and || are fugly and don’t help readability one bit; and and or are much better. About the bitwise operations, since they’re not used often, we don’t see that line-noise often in Python programs.

GOTCHA: (high)
Local variables are never declared. They just *happen*. If a
variable is assigned to, then it belongs to that “scope”. If it’s
only looked at, it may be from the current global scope.

That’s not such a bad thing in my opinion. I can’t recall that ever being a problem for me.

GOTCHA: (medium)
If you have a function that expects two arguments, such as:
def fn(x,y):
return x + y
and you have a tuple of the two elements:
t = (1,2)
You can’t just call that function:
print fn(t)
Or you get a “TypeError: not enough arguments; expected 2, got 1″
error. You have to use this apply() function to get around this
issue:
print apply(fn,t)
3

Considering that apply is gonna be dumped in Python 3000, it’s a better idea to use the * operator. It also makes the meaning clearer:

  >>> def fn(a, b):
  ...   return a + b
  ...
  >>> t = (3, 4)
  >>> fn(*t)
  7

SIMILARITY:
The number zero, the empty string, and the special value None
are all false. Non-zero numbers and non-empty strings are
all true. This is all like Perl. But here an empty tuple ()
is false, as is an empty list [] or an empty dictionary {}.
In Perl, the last two are true if you write them that way, because
they’re references.

From a language design point of view, I never thought that having multiple values evaluate to true and false was a good idea. However, I can’t say that it’s not a useful idiom. In any case, not much to say here, Python hasn’t changed since then.

GOTCHA: (medium)
Assignment is a statement, not an operator. That means that
people are constantly writing loops that aren’t really testing
the exit condition, as in:
while 1:
line = os.readline()
if not line: break
…..
Because they can’t write:
while (line = os.readline()): # ILLEGAL
I feel that this hides the real test condition in a place
it doesn’t belong.

I guess this helps keep the condition of the loop simpler. I don’t really care about this, even though I often use assignments in conditions in Perl.

GOTCHA: (low)
There are no compound assignment operators, like +=.

There are now, thank God.

DISSIMILARITY:
There is no “test at the bottom” loop as in C’s do{}while.

Is that really needed?

DISSIMILARITY:
There is no “three part for” loop from C.

Again, is that really needed?

GOTCHA: (low)
There are no loop labels, and therefore “break” and “continue” are
only through the next level. This encourages the proliferation of
spurious boolean condition variables. It was annoying when Pascal
made you do the same thing. There is no “goto”, which is how C
works around it. As with many things in Python, here you force the
user to be tangled up with exceptions just to do very simple things.
That’s not as clear a solution.

That could be useful. Some languages, like PHP and Ruby, accept an optional integer argument to know how many levels to break out of. The labels remain more readable though (see Damian Conway’s Perl Best Practice for neat examples)

DISSIMILARITY:
Python uses [] to create a new empty list, which you
then reference with [] subscripts.
Perl uses [] to create a new empty array, which you
then reference with [] subscripts.
Python uses {} to create a new empty dictionary, which you
then reference with [] subscripts.
Perl uses {} to create a new empty hash, which you
then reference with {} subscripts.

Larry Wall says “different things should look different” and I guess he thinks hashes and arrays are different. Guido considers them both to be collections and use the same operator to access individual elements. Different philosophies.

GOTCHA: (high)
I had hoped that having *only* reference semantics would spare the beginner
from having to keep in mind the difference between a reference and its
referent as we have in C or Perl. Alas, it is not to be.
x = [1,2,3]
y = x
x[1] = “fred”
print y
[1,"fred",3]

Yeah, this sometimes bites, but once you know that list are references, you learn to be careful when you copy a list.

COOLNESS:
You can “foreach” across multiple items.
a = [ [1,2], [3,4], [5,6] ]
for (i,j) in a:
print i+j
3
7
11

That is very cool and useful indeed. In all fairness, Perl 6 will have something like that.

COOLNESS:
Python’s “longs” are actually built-in, arbitrary precision
integers (i.e. BigInts)

Even cooler, ints and longs were merged a few releases ago, so now ints are automatically promoted to longs when they exceed ints’ range.

COOLNESS: (?)
Named parameters are built into the language. So are
default paramters and variadic ones, although order matters
of course. It’s quite elaborate.

Very useful when you have methods/functions with a lot of parameters and you can’t remember the order.

COOLNESS:
Namespaces seem to be *the* thing in this language. You can always
get a listing of anything’s names/attributes, even all the built-ins
(which you can’t do in perl) or all the names in the local scope
(which you also can’t do in perl).

Namespaces are also simpler than in Perl, one file == one namespace.

DISSIMILARITY:
A class method call gets no class name as its implicit extra
argument the way an instance method would.

Got nothing to say to that.

DISSIMILARITY:
In Python, there is no difference between single and double quotes:
both interpolate C-style escapes like \t, and neither
interpolates variables. In Perl, single quotes do neither escapes
nor variables, but double quotes do both.

Python has raw strings which are used by prefixing an ‘r’ to your string:

  >>> r'\t'
  '\\t'

GOTCHA: (low)
Single and double quoted strings can’t cross line boundaries.
You need triple quotes for that!

It’s not long before you don’t even think about that.

GOTCHA: (medium)
Perl’s hex() function is the opposite of Python’s. Perl’s hex
function converts 22 into 37. Python’s hex function converts 37 into
0×22. Oh my! Like hex, Python has oct() completely backwards from
Perl. Python’s oct(44) returns 054, but Perl’s oct(44) return 36.

I think Python has got it and Perl doesn’t. The operations in Perl are counter-intuitive I think.

DISSIMILARITY:
Because there are no variable markers in Python, there are no
variable interpolation in strings. That makes something like
print “this $x is $y here\n”
become in python
print “this %d is %d here\n” % (x, y)
But since “stringification” is handy, Python usurps backticks
for that purpose:
print “this is”, `x`, “here”
Is going to call the x object’s stringification method (x.__str__)
to get a print value. In perl:
print “this is $x here”
also does so, although the stringification method is oddly named
in Perl.
use overload ‘””‘ => ….

That’s one of the only reason to like sigils. If you really want to, you could use one of Python’s (many) templating packages.

GOTCHA: (medium)
Things that return lists can’t be used where a tuple is expected.
A function that returns a list must be coerced into a tuple to
use this, though.
def fn: return [1,2]
print “this %d is %d here\n” % fn() # ILLEGAL
print “this %d is %d here\n” % tuple(fn())
The illegal part points out that this is an
TypeError: illegal argument type for built-in operation
Which isn’t very helpful.

This can byte you in the butt a few times, but after a while you remember that % wants a tuple.

GOTCHA: (high)
Python has no manpages! The horror!!!!!!!!!!!!!!!!!!!!!!
ENODOC

Well, Python has one man page and it has pydoc, which should give you everything you need.

GOTCHA: (low)
Often Python’s error messages leave something to be desired.
I don’t know whether

I’m not sure I agree here, I never had a problem with Python’s error messages. However, when I forgot a semi-colon in a Perl script once, the message was all but helpful.

GOTCHA: (medium)
All ranges are up to but *not including* that point. So range(3)
is the list [0,1,2]. This is also true in slices, which is the
real gotcha part. A slide t[2:5] does not include t[5] in it.

This is annoying. Python should have irange which would be inclusive. Better yet, make range inclusive and have another function be the exclusive range. When I read range(1, 9) I always think of 1 to 9, then I remember that it’s exclusive, so it’s 1 to 8 actually. That’s not very good, because that’s not how most people think.

SIMILARITY:
Negative subscripts count back from the right. Same as in Perl.

Nothing to say here, except that it’s a nice and useful feature.

DISSIMILARITY:
Python doesn’t convert between strings and numbers unless you tell
it to. And if you don’t tell it to, you get an exception. Therefore
you have no idea what these
x = y + z
x = y – z
x = y * z
are really doing. The first would concat strings or lists, the last would
repeat them. Personally, I don’t care for this, because I always
wonder what subtracting one string or list from another would be.

This is one of the fundamental difference between Perl and Python. Since this is how I’ve always done it, I prefer Python’s explicit conversions, but Perl’s way is useful in small scripts.

COOLNESS:
Python has a reduce() built-in that works somewhat like map().

COOLNESS:
Python’s filter() [same-ish as Perl's grep()] and its map() operators
can operate on items from separate sequences in parallel.

All of these operators are going to be dumped in Python 3000, so better use list comprehensions which have more coolness anyway.

GOTCHA: (high)
This is a big surprise: strings are not atomic (although they are
immutable). They are instead sequences of characters. This comes
up in strange places. For example:
>>> for c in (“weird”, “bits”):
… print c

weird
bits
>>> for c in (“weird”):
… print c

w
e
i
r
d
The second case autosplit the characters! Here’s another:
>>> print map(None, “stuff”)
['s', 't', 'u', 'f', 'f']
>>> print map(None, “stuff”, “here”)
[('s', 'h'), ('t', 'e'), ('u', 'r'), ('f', 'e'), ('f', None)]
(Python’s map None is like Perl’s map $_.)

Well the first example loops over a tuple while the second loops over a string (a trailing comma would be needed to make it a tuple).

GOTCHA: (high)
Because everything is a reference, and there’s no way to dereference
that reference, it turns out that there is no trivial way to copy
a list! This fails:
x = [1,2]
y = x
Because you don’t get a new list there, just a copy to an
old one. Suggested work-arounds include
y = x[0:]
y = x[:]
y = x + []
y = x * 1
This forces people to think about references, again.
So much for lists being first class citizens! Compare
this with Perl’s
@x = (1,2);
@y = @x;
Or even with references:
$x = [1,2];
$y = [ @$x ];
or
@y = @$x;

Python programmers learn pretty soon about copy.copy() and copy.deepcopy().

GOTCHA: (medium)
Slices in Python must be contiguous ranges of a sequence.
In Perl, there’s no such restriction.

I don’t understand what he means here. Help, anyone?

GOTCHA: (medium)
You can’t slice dictionaries at all in Python. In Perl, it’s
easy to slice a hash, and just as sensible.

I’m not sure how useful it is to have literal slices for dictionaries. I would think that when you want to get a few values you get the keys you want in an array, loop over that array and accumulate the values in another array.

GOTCHA: (high)
As we saw with lists, because everything is a reference, and there’s
no way to dereference that reference, this means that again there
is also no built-in, intuitive way to copy a dictionary. Instead,
the suggested work-around is to write a loop:
new = {}
for key in old.keys:
new[key] = old[key]
But this is guaranteed slower, because it’s not at the C level.
It also shows that dictionaries aren’t first class citizens in python
as they are in Perl:
%old = ( “fred” => “wilma”, “barney” => “betty” );
%new = %old;
or even with references:
$old = { “fred” => “wilma”, “barney” => “betty” };
$new = { %$old };
or
%new = %$old;

dictionaries have a copy() method that you can use, and of course the copy module is always available.

GOTCHA: (high)
Lists don’t autoallocate the way dictionaries do. So while this works
on two dictionaries:
new = {}
for key in old.keys:
new[key] = old[key]
This fails on two lists:
new = []
for i in old:
new[i] = old[i]
Because Python refuses to grow the list as it did the dictionary. Perl
will grow both arrays and hashes just fine.

That’s a good thing in my opinion, the programmer must be aware and concious of the length of his array before putting stuff in.

GOTCHA: (medium)
There’s no way to set up a permitted exports list. The caller may have
anything they ask for.

Isn’t it Perl that said there shouldn’t be unnecessary limits in a language? ;-)

GOTCHA: (medium)
You can’t cut and paste these examples because of the issue
of white space significance. :-(

Sure you can, just use one of the many paste services out there. We do it all the time.

GOTCHA: (low)
List objects have built-in methods, like
l.append(x)
But string objects don’t. You have to import
from the string module to get them, and then they’re
functions, not methods.

That was changed in Python 2.0 I believe.

GOTCHA: (low)
You have insert and append methods for lists, but only
a del function. Why is it a function not a method, and
why isn’t it spelled out? Apprently the fashionable wayto do this is
now
a[2:3] = []
Which of course, deletes only one element. ARG.

I must say that it puzzles me that a task like removing an element from a list has its own reserved word. How about list.remove_at(index)?

DISSIMILARITY:
Where Python says “elif”, Perl says “elsif”.

You say color, I say colour.

SIMILARITY:
Neither Perl nor Python supports a case or switch statement, requiring
multiway if’s or a lookup of a function dispatch table.

If you get enough cases that it would warrant a switch statement, maybe a function lookup table is a better idea.

DISSIMILARITY:
Where Python says “break” and “continue”, Perl says “last” and “next”.

Favorite vs favourite.

DISSIMILARITY:
Where Perl says “continue”, Python uses “else” (for a loop block).

Center vs centre

DISSIMILARITY:
Constrast Python:
import os
data = os.popen(‘grep %s %s’ % (patstr, srcfile)).read()
with Perl:
$data = `grep $patstr $srcfile`;

There’s no question that Perl is much friendlier when you want to interact with the shell.

GOTCHA: (high)
Because you can’t use readline() to get a number, people seem to enjoy
calling eval() just to get a string turned into a number:
import sys
str = sys.stdin.readline()
num = eval(x)
This is scary. Even scarier is the propensity for calling input(),
which auto-eval()s its input to make sure it’s the right “type”.
(Funny that a soi-disant “typeless language” should be so danged
picky about this.) That means you see people write:
num = input(“Pick a number? “)
But the person can supply as an answer 0×23 or 2+9 or pow(2,4)
or os.system(“rm -rf *”). I am stunned.

Nobody uses input() these days. Everyone I know uses int() or float() to convert the input to the appropriate type.

GOTCHA: (medium)
The expression 3/4 is 0, which is false. In Perl, 3/4 is 0.75,
which is what you’d expect. You need to force the floatness.
Sometimes Python is just too tied to C, other time not enough.
This is one of the former.

Python 3000 will fix that. In the meantime, you can use from __future__ import division to get the same result as Perl. However, neither language has support for radical numbers for when you do 2/3.

GOTCHA: (low)
Regexes default to emacs style, not egrep style! Gads! This is
surprising to anyone except an emacs weenie. Sigh. And just *try*
to read the manpage on the differences based on passed in arguments
establishing the style. There aren’t any manpages! Have a nice day.

I don’t think that’s true anymore.

GOTCHA: (low)
An out-of-bounds list reference raises an “IndexError: list index out
of range” exception, but not if you use a slice to get at it!
t = range(5)
print t[2:17]
[2, 3, 4]

Hmmm… interesting, I’d never noticed that.

COOLNESS:
Relationals stack:
x < y < z
means
x < y and y < z

That’s a very nice feature. Perl 6 will feature that as well.

GOTCHA: (high)
Python’s lambda’s aren’t really lambdas, because they are only
expressions, not full functions, and because they cannot see
their enclosing scope.

And Guido recently said that they would stay the same in Python 3000.

DISSIMILARITY:
What Perl calls eval(), Python calls exec().
What Perl calls exec(), Python calls os.execv().

More insignificant naming details.

GOTCHA: (low)
Python’s eval() only works on expressions, not full code blocks full
of statements. You need exec() for the whole thing.

I don’t care, I almost never use eval() and I never used exec

GOTCHA: (medium)
This is a tuple of two elements
(1,2)
But this is a tuple of one element:
(1,)
Whereas this is merely the expression 1:
(1)
Yes, the trailing comma counts.

I was recently bit by that problem in Django. It’s a very easy detail to overlook. To be safe, always have a trailing comma in your tuples, arrays and dictionaries.

DISSIMILARITY:
Python uses a pow() function for Perl’s ** operator.
Wait, no, they just added “**” later, with the same
semantics as in Perl, except that something like 2**3**4
in Perl gives you an answer, and in python, an exception.

This must’ve been fixed:

  >>> 2 ** 3 ** 4
  2417851639229258349412352L
  >>>

GOTCHA: (medium)
You can’t just interchange tuples and lists in python the way
you can lists and arrays in Perl. This is an error:
import sys # otherwise, no argv for you, buddy
print “First arg %s and second %s” % sys.argv[1:3]
because you need
print “First arg %s and second %s” % tuple(sys.argv[1:3])

Didn’t we already cover that?

GOTCHA: (low)
I can’t figure out how to write a class destructor for at exit
handling the way I can with Perl’s END{}

See the atexit module.

GOTCHA: (low)
sort and reverse are in-place. This leads to verbose code, such as:
old = [1,2,3]
new = old
new.reverse()
Likewise for sort.

This had bugged me for quite a while. When they finally decided to add sorted, they made it a function instead of a method. Can we get some consistancy here, please?

COOLNESS:
Any function of class can have a significant string called a “doc
string” that can be accessed as whatever.__doc__

Doc strings++

Well those are the points I wanted to cover. Let me know if I made mistakes, or to add anything.

About these ads

2 thoughts on “Python in 1999 and now from a Perl perspective

  1. GOTCHA: (medium)
    The whole mutability versus immutability thing causes many
    confusions. For example:
    t = ([1,2],3)
    t[0][0] = “fred” # legal
    t[1] = “fred” # ILLEGAL
    I don’t understand why tuples weren’t just lists that weren’t
    somehow marked “read-only” or “constant”. That way you
    could do the same with dictionaries or any other data types.

    I’m not sure I ever understood the point of tuples either.

    I’m pretty sure they’re there for efficiency. If you have to construct a collection of a set amount of similar items, tuples are at least more memory efficient and may be slighter faster to construct.

    GOTCHA: (medium)
    Assignment is a statement, not an operator. That means that
    people are constantly writing loops that aren’t really testing
    the exit condition, as in:
    while 1:
    line = os.readline()
    if not line: break
    …..
    Because they can’t write:
    while (line = os.readline()): # ILLEGAL
    I feel that this hides the real test condition in a place
    it doesn’t belong.

    I guess this helps keep the condition of the loop simpler. I don’t really care about this, even though I often use assignments in conditions in Perl.

    In most, if not all, scenarios you can instead do something like:
    for line in os:

    GOTCHA: (low)
    You have insert and append methods for lists, but only
    a del function. Why is it a function not a method, and
    why isn’t it spelled out? Apprently the fashionable wayto do this is
    now
    a[2:3] = []
    Which of course, deletes only one element. ARG.

    I must say that it puzzles me that a task like removing an element from a list has its own reserved word. How about list.remove_at(index)?

    Actually, lists do have pop and remove methods.

    DISSIMILARITY:
    Where Python says “break” and “continue”, Perl says “last” and “next”.

    Favorite vs favourite.

    I never understood why Perl strayed from C on this. I’m glad Python kept true to the loop control statements people know.

  2. jamessan: I prefer break over last, but I prefer next over continue. continue says to me to continue the current iteration while next makes it clear to do the next iteration. I’d like to point out that Ruby uses break and next, which is the combo that makes the most sense IMO.

Comments are closed.