More random python observations from a perl programmer

Michael Hudson mwh21 at cam.ac.uk
Thu Aug 19 13:06:03 EDT 1999


I'm going to be quite picky in some of what follows.

Tom Christiansen <tchrist at mox.perl.com> writes:

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

Python doesn't have a print() function. It has a `print' statement. 
 
> GOTCHA: (high)
>     Local variables are never declared.

*Nothing* is ever delcared in Python.

> 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

Well, no. How would you like to pass a tuple to a function?
 
> 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
>     0x22.  Oh my!  Like hex, Python has oct() completely backwards from
>     Perl.  Python's oct(44) returns 054, but Perl's oct(44) return 36.

This would have to do with strings not being automatically coerced
into numbers, methinks.
 
> SIMILARITY:
>     Constructors and destructors (__init__ and __del__) aren't 
>     automagically called in parent classes in Python.  Neither
>     in Perl.

This sucks, IMHO.

> 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.  

I think this may have changed in the CVS sources. <quick check>, Oh
no, it hasn't. It should.
 
> GOTCHA: (high)
>     Python has no manpages!  The horror!!!!!!!!!!!!!!!!!!!!!!
>     ENODOC

There's info format if you look for it and you have to have a CLI.

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

Is this a joke? This is improving.

> 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 does mean that range(a,b) or t[a:b] has b-a elements in it.

> DISSIMILARITY:
>     This also means that instead of substr() in Perl, you slice a string
>     as in 
> 	s = "string"
> 	print s[2:4]
>       ri
>     Yes, that's all you got.  Strange, eh?  See below on ranges.

What were you expecting?

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

At some point Python will probably support `extended slice syntax'
like so:

range(0,10)[1:4:2] => [1,3]

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

Huh? What would that mean? There's no reason to suspect that the keys
of a dictionary are ordered.

> 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.

new = old.copy()
 
> GOTCHA: (medium)
>     There's no way to set up a permitted exports list.  The caller may have
>     anything they ask for.  

This is a general Python convention; you *can* shoot yourself in the
foot if you try hard enough. Means the debugger can be written in
Python, for one thing.
 
> COOLNESS:
>     DBM files seem (?) to automatically know about nested datatypes.   

Ah, that's pickle. pickle is cool.

> GOTCHA: (high)
>     Scopes don't nest in Python, but they they do in Pascal, Perl, or C.
>     This is supposedly "simpler", but it's very suprising in many ways:
> 	x = 1			# global
> 	def fn1(): 
> 	    x = 10		# implicit local
> 	    def fn2():
> 		print x		# whose?
> 	    fn2()
> 	fn1()
>     The answer is that it prints 1, because fn2 has no x in its local
>     scope, so gets the global.  The suggested work-around is
> 	    def fn2(x=x):
>     using default parameters.

Yes this is a bit annoying. The Right Way to attach state to a
function is a class, of course. Not that that helps much.
 
> 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.

This will change in 1.6.
 
> GOTCHA: (low)
>     You have insert and append methods for lists, but only
>     a del function.  

del statement.

> 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.  

Do people really do that? I don't. It can be made safe:

num = eval(string,{'__builtins__':{}})

but that's a bit hairy. Use float instead.

>     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.)  

Python is more "dynamically typed" than "typeless", I'd say.

> GOTCHA: (low)
>     Regexes default to emacs style, not egrep style!

Not any more. import re.

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

Writing exec() suggests it's a function; it isn't, it's a statement.

> GOTCHA: (medium)
>     Anything that python doesn't like, it raises an exception about.
>     There is no fail-soft.  Even non-exception issues raise exceptions.
>     It's pervasive.  K&P curse languages that force people who want
>     to open() a file to wrap everything in exception code, saying that
>     "failing to open a file is hardly exceptional".  

This is a bit of a flaw, IMHO. Mind you, it forces you to pay
attention.

> 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])

This has been changing over the last year or so. I expect the trend
will continue.

> COOLNESS:
>     I believe that Python checks the prototype signature on methods
>     as well as on functions.  I wonder whether Tim Bunce's ideas run-time
>     evaluation of prototypes for Perl might be able to do this.

You mean perl doesn't? Ouch.

> QUESTION:
>     What is and what is not thread safe?

Everything is, but in a crude way. There's a global interpreter
lock. This isn't the best of situations.

I hope this post has raised the signal-to-noise ratio in the
perl/Python debate (it could hardly lower it).

Michael




More information about the Python-list mailing list