[Tutor] Linear programming - is it bad?
Brian van den Broek
bvande at po-box.mcgill.ca
Tue Aug 10 05:48:57 CEST 2004
Eric Belanger said unto the world upon 2004-08-09 19:30:
> This is my first post here (ive been reading this list for a few weeks,
> interesting content!).
> So my question: is it bad to write a python program/script in a linear
> way? By linear, I mean: the code is executed line by line, without any
> (programmer defined) classes or functions, a bit like [Q]BASIC without
> GOTO's, or , while we're at it, HTML. I could also compare it to Doom 3:
> going from A to B, then C, without any choices or options, until Z.
> I assume that by writing huge projects/programs, im assured programmers
> *needs* to use functions and classes. But since Im beginning, I dont see
> the point.
> Eric Belanger - bilange A gamebox _ net
I didn't take a look at the code you linked to (in the snipp'ed part), and
I'm a relative programming newbie, so read what I say with your skeptic's
goggles on ;-) (Also, having come to the end of writing this email, it
seems I am in verbose-mode today. Oops.)
That said, so far in my own programming efforts, I've noticed two benefits
of functions that I wouldn't want to live without:
1) Program logic that you use time and time again can be wrapped into a
function, and them imported from a module into new code that you create.
As a simple example, in several of my first programs, I found myself
opening a file, calling readlines on it, and then closing the file. Typing
that each time seems silly (and typo-prone). So, without claim that its
the prettiest such function in the world, I put the following into a
module I called fileutils.py:
'''reader(full_file_path) -> file_contents (as a list)
Given a full file path (or a file name in the current working
directory), reader() uses the readlines() method of file objects
to read the lines into a list, closes the file, and then returns
the_file_to_read = open(full_file_path, 'r')
file_contents = the_file_to_read.readlines()
Now, instead of retyping of copying and pasting, I can say
from fileutils import reader as reader
and then invoke the function as though I'd defined it in whatever program
or interactive session I am in. Hassle-free, less to type, and the
debugging was done once and for all.
(This function could certainly be improved (for instance, it could take an
optional mode argument to allow for binary files, and I guess the call-tip
part of the docstring really shouldn't imply a complete file path is
needed), but since it is pretty much what I was typing each time, it saves
me time and effort as is.)
2) Danny's point about functions being for helping humans read is very
important. Write a 10-line function, debug it, and understand what it
does. Then, when your code uses it, if you are trying to work out why
exactly your program is doing some unexpected thing, instead of
conceptually stepping through all 10 lines, you can read the one line with
the function call as an extended Python command that does exactly what you
created it to do. In effect, it packs trusted code logic into a single
line. It's *much* easier to conceptualize your code that way.
Of course both points (1) and (2) depend upon a thorough test and debug of
your function. And, it will surely happen that at some point, while
enjoying benefit (2), you will be unable to see why your program is doing
some unexpected thing. Eventually (and probably at 3am while feeling on
the verge of going completely bonkers), you will be driven to examine some
function very carefully only to discover that you were a bit too quick to
decide that the function def did indeed do exactly what you wanted. That
can make debugging more difficult, but get a trust-worthy, reusable
function and you never have to debug that bit again. (A few such 3am
experiences will certainly teach you to push the boundaries of a function
in every which way while testing it *before* you decide to start importing
it into other programs.)
Note too that you can get some, but not all, of the benefit of (1) by
writing code in a linear manner. If you do it fresh each time, every time
is a new chance to make an error. You might think to get around that by
copying and pasting from the original source. Doing that, you can be
reasonably sure there are no bugs in the lines you paste.
What you cannot be so sure of is that there are no bugs caused by the
interaction of the pasted code and the code it is pasted into. (You must
be much more skeptical that the code does exactly what it is supposed to
do in the new context than you need be of well-tested functions that you
import.) Take, for example, my reader example from above. If I did it in a
linear manner, I'd run the risk that I might put those lines right into
the middle of a program where file_contents (say) already had a meaning.
The names in the pasted code and the pasted-to code might clash, creating
possibly nasty bugs.
Functions avoid that; even if I copy and paste by reader's def block into
a new program context, the name file_contents isn't top-level. Instead,
it's a name in the function's namespace; if I have a file_contents
variable in my top-level code the two can live in peace and harmony, never
stepping upon each other's toes since each has its own namespace. (Trying
to get the same thing by pasting and then manually checking for
name-clashes has two problems: (a) what a hassle!, and (b) you'd still be
vulnerable to introducing new clashes as you extend the code.)
Anyway, that's about 3 times what I set out to type . . . .
More information about the Tutor