Dumb python questions

Andrew Dalke dalke at acm.org
Thu Aug 16 01:43:41 CEST 2001


Paul Rubin:
>Python seems to be in a somewhat dangerous phase of development right
>now, where a lot of the design still isn't worked out, but lots of
>people are using it, which means any mistakes made are hard to undo
>because people's code will break.  Instead, kludges get tacked on,
>potentially resulting in an eventual Perl-like monstrosity

Huh?  "right now?"  There's almost nothing being dicussed except
for '/' which will break even 5+ year old code.  The new keyword,
'yield', is only enabled with a special operation.  Nested scopes
very carefully tried to handle things the right way.  Iterators are
done using new methods not used by old code, and done inside of a
long documented __reserved__ naming convention.  New functions are
added to builtins, which is as expected.  Garbage collection
doesn't break the older reference count semantics.  Deprecated
modules, like regex, are still distributed - as well as very
old SGI specific modules that even I as an (ex-)SGI developer never
used.  The "print >>" trick (yes Alex, I know you don't like it :)
doesn't break existing code.

I think it's a "dangerous phase of development" because this last
year is the first time where Guido could work full-time on Python
development, with others, and not have to worry about fighting
with license agreements.  So it's a major change in the way Python
is developed, and the community hasn't fully worked how how to
deal with that change.  At the same time, more and more people
are using Python (good!) asking reasonable questions comparing
Python to other languages, but not knowing the full reasons that
went into certain decisions.  Reasons that were fleshed out years
ago - long enough that the people who worked on it are not longer
interested in justifying their decisions, but hard to trawl from
the sea of back newsgroup messages.

BTW, people's code may break no matter what is done.  Consider adding
a new global function called 'spam'.  Then the following will
break:

try:
  eval("spam")
  raise AssertionError
except NameError:
  pass

Should it then not be allowed to add new functions?

> (why is there a special type of object for xrange?).

First off, xrange is an *old* type - added in 1993, according to
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/python/python/dist/src/Gramma
r/Grammar

Is your concern that there's a 'range' and an 'xrange', or
that 'xrange' is implemented as a type instead of a class?

I'm guessing the first.  range makes a list of the requested
size and values.  xrange lets you iterate over those values
without builing an explicit list.  It happens that they are
often used in the same way.  There was an attempt to make
xrange more list-like, but as no one used that code (it
contained bugs that no one reported in years) that's been
removed.  People only use xrange as "for i in xrange(..."

The problem is those two do similar things, but there's no word
in English which really captures that difference well.

>If you look at the evolution of Scheme, the designers tried very hard
>to get things right from the start, rather than doing what was
>convenient and fixing it later.

"right" is subjective.  My brain was warped in its formative stage
by years of BASIC programming, so Scheme doesn't slither in as well
as Python.  People who use my code do scientific programming in
Python, C or C++.  Python is a more "right" solution than Scheme.

>Here's something I don't understand: why is it that
>the default value of a function arg is evaluated just once, instead of
>whenever the function is called?  The manual has a warning about that
>and examples of how to program around it, but it seems like poor
>design to need such a warning.

I don't know, but my first thought is on simplicity.
Should the following be legal?

def eggs(a = xyz):
  print a

xyz = "Hello"
eggs()

Currently this fails, since xyz is not defined during 'def eggs'.
If the default value is evaluated at call time, you have to keep
the static scope of the 'def eggs' around.  Especially in pre-2.0
days it would be worse, since if 'eggs' stores its context, that
context also stores a referece to 'eggs', which would cause leaks
in the old-style memory manager.

For that matter, what about

def eggs(a = eggs("Bye")):
  print a
eggs()

Should this print
  Bye
  None
?

Or, when should

def spam(x = 1/0):
  pass
spam()

raise its exception?

Or, suppose you have (taking the code from
http://www.bagley.org/~doug/shootout/bench/ackermann/ackermann.python )

def Ack(M, N):
    if (not M):
        return( N + 1 )
    if (not N):
        return( Ack(M-1, 1) )
    return( Ack(M-1, Ack(M, N-1)) )

def blah(x = Ack(3, 6)):
  print x

blah()
blah()
blah()

If as you suggest the default value is recomputed, then would there
be just as many notes in the documentation pointing out that "the
default arguments are computed every time the function is called"?
And to achieve their needs, wouldn't people have to do workarounds like

_default_ack = Ack(3, 6)
def blah(x = _default_ack):
  print x

just like now when people do workarounds like

def yadda(x = None):
  if x is None:
    x = []

(or for the very picky where you may want x to take on any value,
including None, but if not specified then to create a new list

_default_x = []

def yadda(x = _default_x):
  if x is _default_x:
    x = []
)

Just thoughts.

                    Andrew
                    dalke at dalkescientific.com






More information about the Python-list mailing list