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




More information about the Python-list mailing list