More random python observations from a perl programmer

Evan Simpson evan at tokenexchange.com
Thu Aug 19 21:46:04 CEST 1999


First off, thanks for taking so much time and effort on this, and sharing it
with us.  I'll respond point-by-point, after noting some philosophical
differences.

Python programmers appear to prefer hyperlinked over man-style
documentation, but unlike the GNU Project, we don't have any old manpages to
deprecate <wink>.  Others have pointed out that a great deal of
documentation is available in several formats, but you don't seem to place
any value on any format other than manpages.  I can slightly grok your point
for quick reference purposes, but do you *really* find *any other* format
unusable for exploration and learning of the language?  Enough about that.

Tim Peters once half-joking tripped off a list of principles which describe
the "Python Way".  They go a long way toward explaining the design choices
of Python.  Here are some of them:
  Explicit is better than implicit.
  Simple is better than complex.
  Complex is better than complicated.
  Readability counts.
  Special cases aren't special enough to break the rules.
  Although practicality beats purity.
  Errors should never pass silently.
  Unless explicitly silenced.
  In the face of ambiguity, refuse the temptation to guess.
  Namespaces are one honking great idea -- let's do more of those!

Tom Christiansen <tchrist at mox.perl.com> wrote everything quoted, in message
news:37bc1339 at cs.colorado.edu
> GOTCHA: (medium)
>    The whole mutability versus immutability thing causes many
>    confusions

It needs to be internalized to avoid confusion, yes, but once grasped it's
not troublesome.  Mutability issues and namespaces/dictionaries are closely
related; A major reason that tuples exist is for use as dictionary keys,
since keys require an unchanging hash value, and that means immutability
(pace weird classes).  All namespaces, including those of modules, classes,
and instances, are based on dictionaries.  Lists are actually the only other
mutable (built-in) objects in Python!  It's true that there are several
cases where Python requires a tuple when a list or other sequence should
work just fine, but that is expected to change.

> 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 "<<"
>     and ">>".  It's bizarre that the relational and "?:" are missing.

Python took all (and only) the bitwise operators from C.  Words are
preferred to symbols unless they're well-established from mathematics, IMHO.
It makes sense to Pascal programmers too <wink>.

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

Think of it this way:  Locals are declared *by* an assignment statement (or
param list), unless overridden by an explicit global declaration.  If it's
only looked at, it *must* be from the global namespace.

> GOTCHA: (medium)
>     If you have a function that expects two arguments [...]
> and you have a tuple of the two elements [...]
>   You can't just call that function [with the tuple as argument]

You want automatic implicit tuple unpacking on function calls.  Python
prefers explicit to implicit.  A syntax for expressing explicit tuple
unpacking has been proposed (fn(*t)), and it remains to be seen whether it
will enter the language.

> GOTCHA: (medium)
>     Assignment is a statement, not an operator.
[...]
>     There are no compound assignment operators, like +=.

This is often, and heavily, discussed.  Again, a syntax has been proposed
for Python 2.0 which should garner most of the benefits (set & test in
loops) while avoiding drawbacks (typos, obfuscation).  There are no compound
assignment operators since there are no assignment operators, period.

> GOTCHA: (low)
>     There are no loop labels, and therefore "break" and "continue" are
>     only through the next level.

Agreed that this would be nice, although it's never been much of a problem
for me.

> 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.
[...]
> 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! [later: or dictionary]
[...]
>     This forces people to think about references, again.
>     So much for lists being first class citizens!

This is the whole mutability/namespace issue again.  Since all you *ever* do
is bind names to references, copying *must* be explicit, and dereferencing
is meaningless.  Copying immutable objects is also meaningless, so this is
only an issue for mutable objects.  There's more than one desirable way to
copy complex nested objects, so the copy module is provided. For lists,
"x=list(y)" or "x=y[:]" suffice for shallow copying, while dicts require the
more verbose "y={}; y.update(x)".  If you wanted to be consistent, you could
use "y=[]; y.extend(x)" for lists.  These are not "workarounds", just
different explicit copy methods.  What does any of this have to do with
first-class citizenship?

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

Once again, explicit wins over implicit.  If you accidentally leave the
closing quote off of a string, it's caught at the end-of-line. You can break
single-quote strings over multiple lines by '\'-escaping the newline, and
you can embed newlines with '\n'.  Triple-quoted strings are a convenience.

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

That's because slices specify 'gaps', not elements.  '0' is the gap at the
start of a sequence, 'n' is the gap before element 'n', and '-n' is the gap
before the nth element from the end.  Gaps are defined past the end of the
sequence, so s[:n] takes the first n elements of s, or all of s if it is too
short.  This also allows "s[3:3] = x" to express inserting into the gap
before the third element.  The size of a slice (or range) a:b is b-a.

> GOTCHA: (high)
>     This is a big surprise: strings are not atomic (although they are
>     immutable).  They are instead sequences of characters.

Immutability and atomicity are unrelated, except that atomic things tend to
be immutable.  Strings are *not* sequences of characters, technically; They
are sequences of length-1 strings <grin>.  This may surprise you, but it
comes in very handy, because it means we can apply all of the sequence
concepts, such as slicing.

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

I don't see the difficulty.  If I want to extract a set of ranges, I can
write "s[1:5] + s[10:20] + s[100:120]".  Notational inconvenience?  Or are
you talking about taking every nth element or something?

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

Dictionary keys are conceptually unordered, so they don't act like
sequences, so they don't slice.  It would probably be uncontroversial to
define an operation like Perl's that uses the slicing notation, though.

> GOTCHA: (high)
>     Lists don't autoallocate the way dictionaries do.

Explicit wins, here.  A list element is required to exist before you can
re-bind it.  Class wrappers do exist to provide Perl-like behavior.

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

Python assumes trust.  You can indicate (with a leading '_') that a name
isn't meant for external consumption, but there's no automatic protection
mechanism for determined users to have to find an obscure way around <wink>.
Double leading '_' mangles attribute names to prevent name clashes, but
doesn't render them inaccessible.

> GOTCHA: (medium)
>     Importing a variable only gives you a local copy.  In Perl, it makes
>     two names for the same object.

Mnh?  Importing an object binds it to a local name, it doesn't copy it.  If
what you wanted was a way to re-bind the name *in the original namespace*,
you have to do that explicitly.

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

Lexical scoping is being seriously considered.  In Pascal and C (and Perl?),
though, a nested function can only execute within the scope of a single call
to the containing scope.  A Python function definition is a runtime
operation which produces a new object, which can then be passed back to the
caller and executed at any time, well after the factory function is dead and
gone.

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

That's indentation significance, buster <wink>.  You need to use an editor
with block indent/dedent.

> 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 is a recognized inconsistency, and *will* change.

> 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 way
>     to do this is now a[2:3] = []
>    Which of course, deletes only one element.  ARG.

"del" isn't a function, it's a statement, and a very general one.  I've
never seen the spelling you cite, but always "del a[2]".

> 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
[...]
>    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".

People enjoy being able to type expressions as input, and these methods
should only be used in a trusted context.  If you have a string containing a
literal string, 'int(s)' and 'long(s)' work just fine.  'raw_input("Yes? ")'
should be used to fetch raw strings.

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

It's what you would expect if you assume floating point arithmetic at all
times.  If you're operating on integers, it's quite wrong.  Explicitness
wins, in Python.

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

Slicing returns a sequence, which may be empty.  Element indexing must
address a valid element.  If element access out of range returned None, for
example, how could we tell the difference from a valid access of an element
containing None? Should an out of range access extend the list?

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

Python's lambdas are crippled little sops to functional programmers and
one-line-coders.  Guido is sorry he added them :-)

> GOTCHA: (medium)
[...]
>    Yes, the trailing comma counts [in tuples]

A trailing comma is *always* allowed, in any comma separated list.  Thus
"(1,2)" equals "(1,2,)" and "f(2)" is the same as "f(2,)".  The only time it
makes a difference is at the end of a print statement, or when leaving the
comma off would give you something indistinguishable from a parenthesized
expression.

> GOTCHA: (low)
>     sort and reverse are in-place.  This leads to verbose code

Only if what you wanted was a sorted or reversed *copy*.  Writing a
"sorted()" or "reversed()" function is trivial, if the copying becomes
bothersome.






More information about the Python-list mailing list