REPOST: Re: Newbie Programming
James T. Dennis
jadestar at idiom.com
Sat Dec 29 10:45:15 EST 2001
Paul Rubin <phr-n2001d at nightsong.com> wrote:
> manutd8611 at softhome.net (Evan Roman) writes:
>> I have just begun programming in Python a couple months ago. While i
>> feel like i am doing OK, i don't think im getting any better. The
>> problem is i can't think of anything to program... I think I have most
>> of the fundamentals down, but i can't think of any ways to use them.
>> I was just wondering if there was some website that gives ideas of
>> little projects to do so that one could sharpen his programming
>> skills. Thanx in advance for all the help.
> Think of some other area that interests you--photography, flying,
> music, or whatever. Then think of some programs you'd like to have,
> that are related to that subject. Then write the programs.
Get any old college programming textbook (for any high-level
programming language like C, Pascal, even Cobol, or whatever).
Skip all the stuff about programming in "those" languages and go
right for the "Exercises" sections (usually at the end of each
chapter). Just do the exercises.
Advantage: really old textbooks can be had pretty cheaply. That's
O.K. for your purposes since you're only interested in the
excercises and problems. Of course you can also work on translating
any of the code snippets they give in their examples from "their"
programming language into Python.
Disadvantage: these are likely to focus on procedural programming
rather than OO (object oriented) design. Personally I think people
should have a firm foundation in procedural programming before they
muddy their minds with lots of OO terminology and concepts --- but
others disagree with me and say that learning a procedural approach
will make adopting OO harder later. (That is basically parallel to
the argument that even older "unstructured" programming techniques
impaired one from learning "proper" structured techniques).
If you do go through something like a book on Pascal or C, it will
help if you think of "classes" (implementations of objects) whenever
you see Pascal records or C structs (user defined data types).
The big conceptual difference is that you have to think not only
of the data's structure (the fields and types of components to your
aggregate data types) but also of the operations that are valid
for instances of that data type.
For example if you have a record of people, and a member (field,
element, whatever) called "gender" then it stands to reason that
you'll need functions to set a given person's gender --- presumably
as new instances of this type of variable are created (or
"instantiated" to use the OO terminology). Of course there is the
(somewhat unusual) case of a person's gender changing sometime after
they are born so you might need to also account for that possibility. In
traditional, procedural, programming you'd just worry about the data
structures and you'd have to have all of your code that used those
records/types enforce any rules about the validity values that might get
assigned to them. (Gender might be constrained to "" (unknown) "M"
for "Male" and "F" for "Female" or "G" for "Garcon" and "F" for "Fils"
or whatever).
In OO you'd be encouraged to provide "methods" as part
of the data type (the class). Thus the enforcement of data validity
checking is conceptually localized to the type of data that's being
manipulated. (That's why we might see a code fragment like:
person.setGender("Male") in Python).
Of course we could apply OO principles in writing our programs
in non-OO languages. Some Pascal and C programmers might have
provided a person_setGender() function and applied the discipline
to perform *all* assignment operations on their person.gender data
"member" (field, element, whatever the language at hand calls it)
through this function.
However, this does get more difficult as the number of structures
gets larger and they start to form complex hierarchies of similar
but variant types. Thus the main "objective" of an object oriented
language is to allow this encapsulation of methods and members
(parts of a complex data structure bound to the ways in which they
can be manipulated) and to manage variant types (subclasses) in a
reasonable way. You might say that OO tries to impose methods on
our madness. (O.K., maybe we shouldn't say that).
To give a mildly ludicrous example of how one might need such a
subclass consider the future when we achieve intersteller travel
and we start interacting with hypothetical Thoradians, which as
I'm sure you know have nine different genders. In most other respects
they have they same characteristics as human "persons" (at least in
all the ones that are relevant to our equally hypothetical software
application). Naturally we could list all of the Thoradians as
being of "unknown" gender, or we could assign two or their nine
genders to "male" and "female" and leave the rest as "unknown." Heck,
we could even arbitrarily assign any number of their nine genders to
each of our three categories. However, if other parts of our object
rely on valid values for this field (let us say that we have a
person.salutation() method which returns the appropriate form of
address for Mr. Mrs. Ms. etc. based, in part, perhaps, on the
person's gender), if that is the case then our software's inability
to handle the multiplicity of Thoradian sexual variants might actually
have some business impact on us.
Rather than being mired in our legacy of software, we should be
able to provide a new subclass of persons. In that case most of
the software that was written to handle persons, even most of it
that deals with gender and salutations wouldn't have to be modified.
The setGender() and salutation() methods on the Thoradian persons
could over-ride the simpler and more limited ones on our plain old
Human (Vulcan and Minbari, etc) persons. We can then blithely
call on: new_customer.setGender("Glarn") to handle that new Thoradian
and address our bill thereto using new_customer.salutation() and
the software that was using these data types (objects) would (hopefully)
need little or no modification to support them.
Two other disadvantages to the "old textbook" approach is that it will
provide "toy" problems. That is a set of well-worn old exercises that
are easy enough for a first or second year college (or high school)
student to do alone in less than a week. (That's how homework works).
Also many of these well-worn example problems have been solved by
python modules (Hurrah for software re-use) and thus can be trivially
done with one or two lines of Python. (For example most of the
discussions about building and traversing lists, sorting lists of
strings, and that sort of thing are trivial in Python even if they are
interesting to first year students of Pascal or C).
Even something as simple and innocuous as the old "compute the
average of a sequence of numbers" (which is one of the oldest
and most basic introductory programming exercises) can have
subtle solutions in Python. Here's and example of two different
functions which define "averages" (simple arithmetic means):
#!/usr/bin/python
def average(list):
total = 0; count = 0
for each in list:
count = count + 1 # or count += 1 in Python 2.x
total = total + float(each) # or total += each ''
return total/count
def avg(l):
return reduce(lambda x,y: x+y, map(lambda z: float(z), l)) / len(l)
if __name__ == "__main__":
import sys
args=sys.argv
del(args[0])
print average(args)
print avg(args)
Here we see one version, which is trivially readable and consists
of six lines of code, and another which does the same thing in
a single line of gobbledygook. A subtlety in this example is that
I decided to use sys.argv as my test data set. That returns a list
of strings, so I end up needing to use the float() function to
coerce each element of this list from a string into a number.
(Otherwise I'll build a long string called "total" and try to
divide a string by a number which will throw an exception.
If I make a simple change to my test suite (and require that the
list passed to my functions be numbers rather than strings or anything
else) then the comparison of these two versions of the averaging
function (arithmetic mean, technically) isn't as lopsided:
#!/usr/bin/python
def average(list):
total = 0; count = 0
for each in list:
count = count + 1 # or count += 1 in Python 2.x
total = total + each # or total += each ''
return total/count
def avg(l):
return reduce(lambda x,y: x+y, l) / len(l)
if __name__ == "__main__":
import sys
args=sys.argv
del(args[0])
### convert each to a number
args = map(lambda x: float(x), args)
print average(args)
print avg(args)
... moving the map()'ing into __main__ has allowed a trivial
simplification of the first version (float(each) simply becomes
each) but makes the second one much simpler (to anyone familiar with
lambda functions).
Of course neither of these is object oriented. The problem hardly
lends itself to OO approaches. We could create a class average
with two members (current total and current count) and a few
methods (constructor/initializer that detects the initialization
arguments and (if it's a number) sets the total to that, count = 1
else if it's a sequence (list or tuple) attempts to coerce each
into a float, add that to the total and keep incrementing the count.
Objects of this class would always return their current value as
total / count. They might also overload some common arithmetic
and assignment operators such that code like:
a = Average(6)
a += 2
print a
... would print 4.0 (for example). In some OO languages we could
do something like this. However, it would count as abusive mangling
of operator overloading.
About the only advantage to some "averaging" class would be that
objects in that class could keep track of their own count, so that
functions calling on them could add values to them, and get the
current "running" average without regard for how many values have
been added to the average. (This, in general, describes the problem
with introducing OO too early in teaching programming: it imposes
an additional layer of complexity and abstraction which becomes
useful as problems scale beyond one point, but are an impediment
up until then).
========= WAS CANCELLED BY =======:
Path: news.sol.net!spool0-nwblwi.newsops.execpc.com!newsfeeds.sol.net!newspump.sol.net!newsfeed.direct.ca!look.ca!newsfeed.dacom.co.kr!feeder.kornet.net!news1.kornet.net!ua4canc3ll3r
From: "James T. Dennis" <jadestar at idiom.com>
Newsgroups: comp.lang.python
Subject: cmsg cancel <a0koeb$2o7u$1 at news.idiom.com>
Control: cancel <a0koeb$2o7u$1 at news.idiom.com>
Date: Mon, 31 Dec 2001 02:03:01 GMT
Organization: A poorly-installed InterNetNews site
Lines: 2
Message-ID: <cancel.a0koeb$2o7u$1 at news.idiom.com>
NNTP-Posting-Host: 211.57.49.2
X-Trace: news2.kornet.net 1009774730 27193 211.57.49.2 (31 Dec 2001 04:58:50 GMT)
X-Complaints-To: usenet at news2.kornet.net
NNTP-Posting-Date: Mon, 31 Dec 2001 04:58:50 +0000 (UTC)
X-No-Archive: yes
X-Unac4ncel: yes
X-Commentary: I love NewsAgent 1.10 and the Sandblaster Cancel Engine Build 74 (19 March 1999)
This message was cancelled from within Mozilla.
More information about the Python-list
mailing list