More random python observations from a perl programmer

Fredrik Lundh fredrik at pythonware.com
Fri Aug 20 04:34:37 EDT 1999


(posted and mailed)

just some random comments on tom's
random comments:

Tom Christiansen <tchrist at mox.perl.com> wrote:
> 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.

"in" is a sequence operator; it's supposed to loop over
a sequence object (an ordered collection). dictionaries
are mappingss, not ordered collections.  some extension
types provide collections that are both sequences and
mappings.  the archives have more info on this.

> 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

and praise Guido for that one...

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

except that experienced Python programmers
write that as:

    while 1:
        line = os.readline()
        if not line:
            break

and use advanced pattern matching mechanisms to
read that as single construct.  see the archives for
more info.

(contrary to popular belief, few lines doesn't always
mean better code...)

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

strings (single and double quoted strings are fully equivalent)
obey the same rule as all other python code: they won't go
over multiple lines, unless you add a backslash to the end of
the line.

print "spam"\
    "egg"

works, in other words.

btw, note that Python automatically concatenates string
literals:

print (
    "spam"
    "egg"
)

> DISSIMILARITY:
>     There are no private variables in a module or a class.
>     They can always be fully-qualified and accessed from
>     without.

unless you use things like Bastion, mxProxy, et al.

and the "__" prefix can be used to get private scoping,
of course.

> 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.  
> 
> GOTCHA: (high)
>     Python has no manpages!  The horror!!!!!!!!!!!!!!!!!!!!!!
>     ENODOC

well, since I've hardly ever heard anyone else asking for them.
maybe you could help us out -- I doubt it would be a major
effort to write an "esis2man" converter (look under
Doc/tools/sgmlconv).

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

agreed.  they often expose too much technical stuff (how
many python programmers know what a read-only buffer is,
for example).  but this is a quite complex issue...

> 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

it's not a copy.  it's just another reference, under a
new name.  the target object doesn't know anything
about this...

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

I don't really understand the second sentence here,
but never mind...

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

sensible?  what are the semantics?

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

maybe not intuitive, but:

x = y.copy()

is pretty clear when you see it.  as are:

from copy import copy, deepcopy

x = copy(y)

note that all the above return shallow copies.  if
that's not what you want, use deepcopy instead.

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

now he does that again.  what's your definition of "first
class citizen"?  is Python objects disqualified just because
the (virtual) common class ancestor doesn't implement a
standard "copy" method?

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

as in Python.

it's the assignment statement that doesn't work as you think.
look again...

> 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 most likely change in 1.6 (which introduces
Unicode strings, among other things).

> 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 a[2]

(del can affect the active namespace, which is
why it's a statement and not a function).

there's also a remove method, of course, but that's
a slightly different thing.

> GOTCHA: (medium)
>     The expression 3/4 is 0, which is false.  In Perl, 3/4 is 0.75,
>     which is what you'd expect.

depends on who you are, as earlier threads on this topic have
shown.  on the other hand, I'm pretty sure Guido won't make
this mistake the next time he creates a new language ;-)

fwiw, my copy of perl refuses to answer, though.  maybe
it's broken?

[fredrik at brain tools]$ perl  
3/4




^D
[fredrik at brain tools]$

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

$ lynx www.python.org/doc/current/lib/module-re.html

obviously, there are more perl weenies out there than
posix weenies...

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

yup.  a slice is not a given set of elements.  quoting
the language reference:

    a[i:j] selects all items with index k such that i <=k < j. 

which is exactly what's going on here...

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

and accordingly, the world if full of programs
that does something like:

    fh = open(backupfile, O_APPEND|...)
    write(fh, data)
    close(fh)

    remove(originalfile)

btw, if you don't want open to raise an exception, use
os.path.exists to test for the existence (better test be-
fore than after, as we all know).

if you do this, you'll still get an exception sometimes, but
only under exceptional circumstances!

(Who's K&P?  Kuchling and Peters?)

> 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 is changing, as more code is starting to use the
abstract API.

on the other hand, there's a reason why the string
formatting operator is picky about this.  consider this:

> print "Arguments are %s" % sys.argv[1:3]

and then think a while about how things would work
if you'd replace sys.argv[1:3] with a user defined object,
and changed the string formatting operator to accept
any sequence object.

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

http://www.python.org/doc/FAQ.html#6.20

> GOTCHA: (low)
>     You have to compiled the definition for a  function before you may
>     compile the call it. This is ok
>
> def fn(): 
>     print "hi"
> fn()

this places something called "fn" in the current namespace (the
global one, if you run that code as is), and then refers to it.

>     But putting the fn() call first:
> fn()
> def fn(): 
>     print "hi"
>     Produces the ever helpful: 
> NameError: fn
>     error message.

and this attempts to refer to something called "fn" in the current
namespace, which doesn't exist.

> So much for autoloading.

what's autoloading gotta do with it?

here's how things really work:

    1) Python compiles the *entire* module into byte codes.
    2) Python *executes* that byte code in the module's name space

when you execute "fn()", Python looks for something
called "fn" and calls it (where fn could be a function,
a class, a bound method, a lambda expression, or a
callable object instance).

when you execute "def fn", Python sets "fn" to point
to a function object.

(yes, def's a statement, looking a lot like if and while et. al.,
and behaving a lot like assignment and import).

>     ADDENDA: It turns out that def() happens *AT RUN TIME* in python.
>     That's the root of this problem.  In Perl, a sub{} is a compile-time
>     thing.

python's a dynamic language, you know ;-)

> GOTCHA: (low)
>     When you need to make an empty copy of the same type, you write
> x = y[:0]
>     So much for being typeless. Sigh.

sorry, I don't understand that comment...

(maybe because Python's not a typeless language. objects
are strongly typed, but name don't imply types; a name can
refer to different kinds of things at different times...)

> SIMILARITY:
>     Python uses None in many places where Perl uses undef().
>     But not all.  You can't over-subscript an array and get
>     away with it.

and unfortunately,

None, spam = 1, 2

doesn't really do what you would expect...

</F>





More information about the Python-list mailing list