Unification of Methods and Functions

James Moughan moughanj at tcd.ie
Mon May 17 23:11:44 EDT 2004


David MacQuigg <dmq at gain.com> wrote in message news:<4ka7a0t585802d81i417j5ufmrdjbq6hei at 4ax.com>...
> I'm beginning to understand what you are saying.  Thanks for hanging
> in there.
> 
> On 12 May 2004 20:51:20 -0700, moughanj at tcd.ie (James Moughan) wrote:
> 
> >David MacQuigg <dmq at gain.com> wrote in message news:<q313a0lroe1uiiu43nkhhnqnbcm4jhgkrk at 4ax.com>...
> >> On 10 May 2004 16:53:06 -0700, moughanj at tcd.ie (James Moughan) wrote:
> >>
> >> >David MacQuigg <dmq at gain.com> wrote in message news:<889t90tdl9o9t25cv5dj6k5rnktuce0jin at 4ax.com>...
> >> >> On 8 May 2004 07:07:09 -0700, moughanj at tcd.ie (James Moughan) wrote:
> >>
> >> < snip topics we have finished >
> >>
> >
> >As far as I understand it, this is fairly different; the way I would
> >implement it, each cell would be an instance, not a class.  I would
> >have to look at some kind of code/spec to really know, though.  Do you
> >have an example of a piece of software were you follow this approach?
> 
> Using instances instead of classes is something I hadn't considered.
> I could add one line after each class to instantiate it.  I'll have to
> play with this a bit, and see if I can come up with a *simple* program
> to do the same thing as Animals_2, but using instances.  I don't yet
> have the code written for my circuit-design platform, but I can give
> you a spec that describes some of the needs I see.
> 
> 1) We need to represent a hierarchy of cells, typically 5 to 10 levels
> deep.
> 2) Each cell has data associated with it.  Some of this data is a
> cummulative total of similar data from all subcells -- total currents,
> total chip area, etc.
> 3) The data for each cell is unique, and it must be displayed in a
> format unique to that cell.
> 4) We need a simple function, show() that can be called for any cell
> or no cell at all (just knowing the cell type).  This function should
> show the cell data and the data from all cells above it.  In case no
> cells of a particular type have been added to the design, the
> cummulative data should just show a default value, like zero.
> 
> In addition to these functional requirements, we need to make the
> program structure as simple as possible, to use as an introduction to
> OOP for design engineers who have no CIS background, but have learned
> Python up to the point where OOP is introduced.
> 
> Robustness and generality are not prime requirements at this point,
> although introducing something like _private variables is OK, because
> it won't distract from the introduction.
> 

I think another poster gave a sample architecture for this type of
thing - on a brief look it looks sound, and since he's an electronic
engineer I'd say he probably understands the precise problem better
than I do.

> >> I'm sure there are other examples from other specialties.  In
> >> accounting, I can imagine a hierarchy of accounts, with a total for
> >> each account including all of its subaccounts.  Don't just assume that
> >> the problem isn't real because you haven't encountered it in your
> >> work.
> >
> >Sorry, but that's not how you would do an accountancy package in OO. 
> >Again each account would be an instance, not a class, otherwise adding
> >a new account would require either code modification or metaclass
> >hacking.  Instances scope down, and having a count of the sub-accounts
> >stored in an object would be OK in my book.  Though you wouldn't need
> >to, since the container for the instances would do it for you.
> 
> Seems like this is very much like the Animals example.  We create an
> instance for each asset, but there is one Asset class which is not an
> instance.  You can't transfer funds to the Asset class, only one of
> its accounts.  The Asset class, like the Mammal class, keeps data that
> is representative of all instances of the class.
> 

Again, no - can you imagine how disasterous out-of-synch is in
accountancy?  It would be better to physically destroy the hardware
than cause an OOS error.  I'd keep all of the assets in a container
and add their values when the number is needed  -effectively
re-implementing a database.

> >But even this isn't how you would organize an accountancy program. 
> >You'd use a database.
> 
> That would not work as an example introducing OOP.  It would be like
> saying to the students -- You can't understand OOP, so we're not even
> going to try.  Just learn this database program, and use it to solve
> whatever problems you may encounter.
> 

We were specifically talking about real world examples - if there's no
need to use this structure then there's no need to teach it.  Instead,
you could choose examples reflecting how OOP is generally used.

> >>
> >> <snip>
> >>
> >>
> >
> >70 -> 30 because of this?  Really?
> 
> I would also cut way back on the "motivational" parts, and a number of
> other things that fit well in the current presentation, but are not
> necessary in the presentation I have in mind.  My 30 page estimate is
> based not on crossing out topics in Learning Python, but on looking at
> the 7 pages I have now, and guessing what more I need to add.
> 
> One of the best books I've ever read on a complex subject is
> Introduction to Quantum Theory by David Park.  The book is split into
> two parts: the theory, which is presented in a brief, straightforward,
> but unrushed manner; and the examples, which amplify and re-iterate
> what is in the theory section.  The first time through, you need to
> spend a lot of time reading the examples along with the theory.  Years
> later, you can quickly read just the theory section, and it is a real
> pleasure.
> 
> I think I can present the "theory" of OOP in 7 pages, at a sensible
> pace, including everything I think will be needed to write most
> programs ( bound and unbound methods, lambdas, static methods, etc. ).
> http://ece.arizona.edu/~edatools/Python/Prototypes.doc  Adding
> examples and exercisise may take another 25 pages.  Adding
> supplementary topics ( multiple inheritance, method resolution order,
> some of the techniques you are showing to make classes more versatile
> and robust, etc. ) may take another 30 pages.
> 
> >Anyway, I guess you know what you have time to do, it being your
> >course 'n all. :)
> >
> >> <snip>
> >>
> >> >>
> >> >> What data are we talking about?  numMammals is specific to Mammal.
> >> >> genus is specific to Feline, but *inherited* by instances of a
> >> >> subclass like Cat.
> >> >
> >> >The numAnimals etc... data, which is stored in Animals but gets
> >> >arbitrarily altered by the actions of subclasses of Animal, and
> >> >therefore is not specific to animal; it doesn't represent the state of
> >> >the Animal class or of Animal objects, but of a whole bunch of
> >> >subclasses of Animal.
> >>
> >> The total current to an IC is the sum of the currents to all of its
> >> subcircuits.  That current is a single number, for example, 35
> >> microamps.  It has a name "Iss".  Iss is a characteristic of the IC
> >> which appears in data sheets, etc.  It is a variable representing the
> >> state of the entire IC.  It does not represent the state of any
> >> subcircuit in the IC, even though it gets "altered" whenever one of
> >> those subcircuit currents changes.
> >
> >So the IC is an instance which stores or accesses data about the
> >instances which it contains, i.e. circuit elements; not a class which
> >you sub-class to get subcircuit elements.  I'm discussing classes and
> >class heirarchies, not instance heirarchies.
> 
> I was also assuming a class hierarchy.  The idea of a hierarchy of
> instances is new to me.  I guess I'll need an example to know what you
> are talking about.
> 

I just mean that one instance contains other instance, which contain
further instances... to access an instance, access it's container. 
I'm sure you're familiar with this.

> >> Looks like this whole argument comes down to what we mean by the word
> >> "specific".  Let's drop it and focus on the more interesting topics in
> >> this thread.
> >
> >From here, it looks like the problem is the difference between an
> >instance and a class?
> 
> ???
> 
> >> >>
> >> >> These are normal programming errors that can occur in any program, no
> >> >> matter how well structured.  I don't see how the specific structure of
> >> >> Animals.py encourages these errors.
> >> >
> >> >Imagine if your structure had been implemented as one of the basic
> >> >structures of, say, Java.  That is, some static data in the Object
> >> >class stores state for all the subclasses of Object.  Now, someone
> >> >coming along and innocently creating a class can break Object -
> >> >meaning that may break anything with a dependency on Object, which is
> >> >the entire system.  So I write a nice GUI widget and bang! by some
> >> >bizzare twist it breaks my program somewhere else because of an error
> >> >in, say, the StringBuffer class.  This is analagous to what you are
> >> >implementing here.
> >>
> >> I'll need an example to see how these general worries can affect the
> >> Animals_2 hierarchy.  What I see is quite robust.  I added a Feline
> >> class between Mammal and Cat, and I had to change only two lines in
> >> the Cat class.  ( And I could avoid even that if I had used a "super"
> >> call instead of a direct call to the Mammal functions.)
> >
> >And if someone unfamiliar with the code neglects to call the
> >superclass initializer when they create a new animal, then the code
> >will break in unpredictable places.  Not calling the superclass is a
> >common problem - but easily fixable, *providing* the effects show up
> >in the subclass, and not at some other random place in the heirarchy.
> 
> Adding a call to a superclass __init__ is a common pattern in the
> examples I've seen.  See the section "Calling Superclass Constructors"
> starting on page 321 in Learning Python, 2nd ed.
> 

Yup; people still forget the simplest things.  *Especially* the
simplest things, in fact.

> We need to make a distinction between users and programmers in our
> expectations of what kind of errors they will make.  Users only need
> to *read* a class definition and understand what it does.  Programmers
> are the group that needs to remember to add a call to the superclass
> when they write a new class.
> 
> I am a user of the Qt Toolkit, but I would not attempt to add a class
> to their existing hierarchy.  Nor would I expect Trolltech to provide
> me with some kind of robust class-generating function that was
> guaranteed to generate an error-free class at any point in their
> hierarchy I might chose to insert it.
> 

If you're writing code, you're writing it to be modified at some
point, unless it's a few lines-long script.  If the modifications are
made by someone else then they will reasonably expect that the code
doesn't contain implicit deathtraps, like non-local data.

> >>
> >> We are modeling the real world here.  When you add a lion to a zoo,
> >> you add one to the count of all animals.
> >
> >When you add a lion to a zoo, you add one entry in the lion table of
> >your database.  When you want to know the number of animals in the
> >zoo, the database query counts the number of entries in all of the
> >animal tables.  *Real* databases are built that way because of
> >experience; repetition of data invariably causes out-of-synch
> >problems.
> 
> I think the database problem is different than our Animals example.
> With a database, you typically have many users directly changing the
> data over a long period of time when errors can accumulate.  Maybe
> some user runs a "transaction" script to debit one account and credit
> another, but his line goes down before the second part completes.  So
> it makes sense in this case to store only the primitive data, and
> accept the overhead of recalculating everything else from that data
> whenever it is needed.
> 

Solving the problem of atomic transactions is something else, and will
make your head spin. The accepted design restrictions on databases are
not specific to examples using concurrent transactions.

The database problem is really no different to the case of a zoo - we
want to store a bunch of data about instances/entries of types/tables.
 Databases just make this much more natural and robust.

> >I worked on a project a couple of years ago to port a legacy database
> >into MySQL, and it was made damned close to impossible by this sort of
> >thinking; how the heck do you port a database which has had it's
> >shortcomings patched over to the point where it depends on the
> >inconsistent results for it's output? :-\
> >
> >> When you add 2 microamps to
> >> the core currents in a bandgap voltage reference, you add that same 2
> >> microamps to the total supply current.
> >>
> >
> >Again, I'd be inclined to handle this problem at the instance level,
> >not the class level.
> >
> >> I'm no expert in OOP, but what I have seen so far is not near as clear
> >> in structure as the origninal Animals_2 example.
> >>
> >
> >When you have to troll through a 20K line program where the
> >functionality for a single subsystem is scattered through multiple
> >classes in a heirarchy, it puts a really different perspective on what
> >'clear structure' is.  Likewise, when everything is modularized
> >explicitly and you can alter a complex system which you barely
> >understand to include completely new core functionality in an hour or
> >two of work.
> 
> I would expect a Python programmer seeing the Animals_2 example for
> the first time, could add a class between Feline and Mammal by the end
> of an hour, and be quite confident that nothing was broken.  I studied
> your example for two hours, and I still don't understand it well
> enough to make some basic changes.
> 

Yes, the code is too complex - I don't normally use run-time
metaprogramming to keep a count of cows :) - but that's because I was
straining to find a good solution within the paradigm you had set.

I'd expect a Python professional to undestand the example I gave
fairly quickly, though - but then again, most of the time they will
not need to.

> >
> >You're right, I don't give a damn whether classes model the real
> >world; what matters, in general order of preference, is:
> >
> >- Robustness
> >- Maintainability
> >- Programmer time
> >- Efficiency
> 
> This is a good set of priorities for a production program.  For
> teaching OOP, I would say clarity is the over-riding concern.
>

Sure.  But close to that should be examples which reflect on the way
you would actually solve a problem.
 
> >I will sell any theoretical principle to the salt mines for those. 
> >'Classes-model-the-real-world' is an abstract theory, an evolutionary
> >holdover from when OO was the Next Big Thing in AI.  If it leads you
> >to design a non-robust, hard to maintain system then, in my book, it
> >gets dropped.
> >
> >> I'm reminded of the criticism Linus Torvalds got when he first
> >> published Linux.  The academic community thought it was the worst,
> >> most fundamentally flawed design they had ever seen.  It did not fit
> >> some expectation they had that a "microkernel" architecture was the
> >> proper way to design an OS.  Luckily, Mr. Torvalds was not dependent
> >> on their approval, and had the confidence to move ahead.
> >
> >I've been trying to explain why the pattern in your example will cause
> >bugs; you've been justifying it in terms of OOP metaphors. *shrug* The
> >comparison is clear enough to me... :)
> 
> I find the "piece of machinery" metaphor to be helpful, but no big
> deal.  Mostly, it re-inforces the idea of encapsulation, keeping the
> dirty details of how an alternator works inside the component, and
> presenting to the outside world, a simple interface: if you want more
> current at that output, put more voltage on this input.
> 
> I'm not using metaphors to justify bad programming.  I am just having
> trouble seeing how one program, side-by-side with another, is
> supposedly bad.
> 
> >> >> >> I'm not sure what you mean by "side effects" here.  The show()
> >> >> >> function at each level is completely independent of the show()
> >> >> >> function at another level.  >
> >> >> >
> >> >> >But the inventory data isn't independent.  It's affected by classes
> >> >> >somewhere else in the heirarchy.  Worse, it's done implicitly.
> >> >>
> >> >> The "inventory data" actually consists of independent pieces of data
> >> >> from each class. ( numCats is a piece of inventory data from the Cat
> >> >> class.)  I'm sorry I just can't follow this.
> >> >>
> >> >
> >> >numMammals OTOH is not just a piece of data from one class - it's a
> >> >piece of data stored in one class, but which stores data about events
> >> >in many different classes, all of which are outside it's scope.
> >>
> >> Exactly as we see in objects in the real world.
> >
> >Objects perhaps, but not classes.  This seems to be the distinction
> >which is driving this whole problem; you are regarding extending a
> >class as if it were adding a member to an instance.
> 
> ???
> 
> >> >That's the way it has to be, if you want to write it like that.
> >> >However there is nothing to say that a given problem must use a
> >> >certain class structure.  If you come up with a solution like this
> >> >then it's near-guaranteed that there was something badly wrong with
> >> >the way you modelled the domain.  Either the program shouldn't need to
> >> >know the number of instances which ever existed of subclasses of
> >> >mammal or else your class structure is wrong.
> >>
> >> Trust me, the need is real.  We just need to find the optimum example
> >> to show how Python solves the problem.
> >
> >Mail me some code where you need to do this in a real system, and I'll
> >show you how to refactor it. :)
> 
> Let's see if we can get the Animals_2 example right, based on the
> requirements I stated above.  It will be a few months before I have my
> design platform ready, and it will be far more difficult to discuss
> basic ideas in the context of a big system.  I think Animals_2.py has
> one of everything I will be using.
> 
> >> In my work as a software product engineer, I've learned to deal with
> >> two very common criticisms.  1) The user doesn't need to do that.  2)
> >> The user is an idiot for not understanding our wonderful methodology.
> >> These are generally irrefutable arguments that can only be trumped by
> >> a customer with a big checkbook.
> >>
> >> I generally don't engage in these
> >> arguments, but on one occasion, I couldn't resist.  I was trying to
> >> show an expert how a complicated feature could be done much more
> >> easily with simpler functions we already had in our program.
> >>
> >> His argument was basically -- every expert in this company disagrees
> >> with you, and you're an idiot for not understanding how our new
> >> feature works.  I replied that I was the one who wrote the User Guide
> >> on that feature.  He started to say something, but it was only a
> >> fragment of a word, and it kind of fell on the table and died.  There
> >> was a few seconds of silence, while he tried to figure out if he could
> >> call me a liar.  I just looked right at him without blinking.
> >>
> >> Forget what you have learned in books.  Think of a real zoo.  Think
> >> how you would write the simplest possible program to do what Animals_2
> >> does -- keep track of all the different classes of animals, and
> >> display the characteristics of any animal or class, including
> >> characteristics that are shared by all animals in a larger grouping.
> >
> >What you're asking is how would I implement something simply,
> >providing that I have to implement it with method X.  Give me a
> >functional spec - what the system needs to do from the user point of
> >view - and I'll tell you how I would do it.
> 
> See above for functional spec.
> 
> >In the case of a real zoo, I can pretty much guarantee that it would
> >begin
> >
> >>>>import PySQLdb
> 
> Don't forget.  My basic purpose with the Animals_2 example is to teach
> OOP, not the use of a particular database.
> 

The example should follow the purpose, though - find an example which
reflects accurately on good OOP and it will make things considerably
clearer.

> >> >And, as general rule, you should think carefully before using classes
> >> >to store data; that's typically what objects are for.  I used static
> >> >data in programs quite a lot before I realised that it too-often bit
> >> >me later on.
> >>
> >> Classes *are* objects.
> >
> >In Python, classes are 'first class objects' - you can pass them
> >around as references, alter them and so on.  That's in the same way as
> >functions are 'first class objects'.  That's a different and older
> >piece of terminology than the term 'object' in OOP, which unhappily
> >coincides with it.
> >
> >Wherever I can, I'll convert the discussion over to use the term
> >instance if you're more comfortable with the terminology, but in the
> >canonical definition a class is not an object.  Objects get created
> >from classes, and the term is equivalent to instance for practical
> >purposes (though you get into occasional pieces of terminology like
> >'object instance' where it's just convenient to not have to say
> >'object object', and 'object variable' would get kinda confusing too.)
> > Check any basic text on OOP for confirmation, say
> >http://java.sun.com/docs/books/tutorial/java/concepts/
> 
> I agree the terminology is confusing, and I try to use "object" only
> in the most generic sense.  "Class" and "instance", as Python uses
> them are OK with me.
> 
> The question, as I understand it, is whether classes should hold just
> methods, or both methods and data.  Again, the examples I have seen
> show plenty of data being stored as attributes of classes.  See
> Learning Python, 2nd ed. p. 317.  The general form of the class
> statement according to Mark Lutz is:
> 

No, that's not the issue.  Classes can certainly store data, though
you should hesitate a little before doing it; the problem is that
you're using classes to store non-local data, in particular data about
subclasses of the class.  As I said, keeping a count of Mammals in
Felines in Feline would be fine, if odd for most applications; keeping
that count in mammals (and accessing it through Bovine!) is a bug
waiting to happen.

> class <name>(superclass, ...):   # Assign to name.
>    data = value                  # Shared class data
>    def method(self, ...):        # Methods
>       self.member = value        # Per-instance data
> 
> Methods are more prevalent in the examples than data, but as far as I
> know, there is no warning to the effect: This is possible, but not
> good practice.
> 
> >> I think you mean instances.  I make a
> >> distinction between class variables and instance variables, depending
> >> on whether the variable is different from one instance to another.
> >> Every instance has a different cat.name, but all cats share the genus
> >> "feline".  In fact, they share that genus with all other members of
> >> the Feline class.  That is why I moved it from Cat to Feline as soon
> >> as our example was big enough to include a Feline class.
> >>
> >> >
> >> >OK: start with the basics.  We need iterative counting data about the
> >> >individual elements of the heirarchy.
> >> >
> >> >The first thing is that we need to factor out the print statements.
> >> >Your back-end data manipulation modules should never have UI elements
> >> >in them.  So, whatever form the data manipulation comes in, it should
> >> >be abstract.
> >>
> >> You are adding requirements to what I already have.  OK if it doesn't
> >> slow the introductory presentation too much.
> >>
> >> >Secondly, we want to keep the data stored in each class local to that
> >> >class.  So, Mammal can store the number of Mammals, if that turns out
> >> >to be a good solution, but not the number of it's subclasses.  OTOH we
> >> >could remove the data from the classes altogether.
> >>
> >> Think of a real zoo.  If you ask the zookeeper how many animals he
> >> has, will he tell you only the number that are animals, but are not
> >> also lions or tigers or any other species?  That number would be zero.
> >>
> >
> >In a real zoo, it would not require brain-surgery on the zookeeper to
> >introduce a new type of animal. :)
> 
> Adding the Feline class to the existing hierarchy was very easy.
> 

But it required modification of the program code, and would require
modification each time you wanted to add a new animal.  Better to
avoid that complexity for your user - unless you're in the business of
selling updates.

> >Also, as I say, in a real zoo you would use a database for this task. 
> >Any good database design person would balk at the idea of even storing
> >the number of a particular animal as an explicit variable in the
> >database.  Think about that.
> 
> Just so we can move on, I will accept your assertion that a truly
> bulletproof zoo program would use a database.  We still need an
> example that doesn't use a database, or anything other than Python's
> basic functionality, to teach students about OOP.
> 
> >> I really do want numMammals to display the total number of all
> >> mammals, whether or not they are a member of some other class in
> >> addition to Mammal.
> >>
> >> If I were to guess at your objection to this, I would assume you are
> >> worried that the different counters will get "out-of-sync", if for
> >> example, someone directly changes one of these variables, rather than
> >> calling the appropriate functions to make a synchronized change.
> >>
> >> My answer to that is to make the counter variables private.  I've
> >> added a leading underscore to those names.  numMammals is now
> >> _numMammals.
> >>
> >
> >OOS due to an explicit variable alteration is pretty much the
> >worst-case scenario, yes.  It would require quite an incompetent coder
> >to do it, but it could still easily happen.  The more likely problem
> >is the failure to call the superclass constructor - which may even be
> >harder to debug, since at least in the other case you can run through
> >the program in a debugger and find where the variable is being
> >changed, or just grep numMammals.
> >
> >Your faith in private variables is touching. :)
> 
> The Python policy on private variables, which I agree with, is that we
> don't need to prevent deliberate action, just make sure the user
> understands a variable is private, not to be altered except by the
> provided functions.
> 

Oh, I agree - when people want to access private variables they often
just delete that annoying little 'private' word.  The __ syntax at
least gives something searchable.

Generally, though, it's better to solve a problem through making the
program *necessarily* do what you want than through depending on
conventions to save the design.

> >> >Thirdly, it would probably be nice if we had the ability to implement
> >> >the whole thing in multiple independant systems.  Currently the design
> >> >only allows one of "whatever-we're-doing" at a time, which is almost
> >> >certainly bad.
> >>
> >> ???
> >
> >Supposing I want to store data for two zoos.  If the data on the
> >number of animals is stored at class-level, then I literally can't do
> >that without closing down the program and opening up a different data
> >set.  Munging the code to have numMammals_at_zoo_1 isn't a good
> >solution either. :)
> 
> OK, now I understand.  Let's make "Multi_Zoo.py" an example later in
> the chapter, not part of the introduction.
> 
> >> >After a bit of brainstorming this is what I came up with.  It's not a
> >> >specific solution to your problem; instead it's a general one.  The
> >> >following class may be sub-classed and an entire class-heirarchy can
> >> >be placed inside it.  It will then generate automatically the code to
> >> >keep a track of and count the elements of the class heirarchy,
> >> >returning the data you want at a method call.
> >> >
> >> >This is done with a standard OO tool, the Decorator pattern, but
> >> >ramped up with the awesome power of the Python class system. :)
> >>
> >> My non-CIS students are not familiar with the Decorator pattern.  I
> >> fear that will make this example incomprehesible to them.
> >>
> >> >
> >> >The above code is 51 lines with about 10 lines of comments.  For a
> >> >project of any size, this is a heck of an investment; I believe it
> >> >would take a fairly determined idiot to break the system, and *most
> >> >importantly*, they would be able to trace back the cause from the
> >> >effect fairly easily.
> >>
> >> This is an impressive bit of coding, but I can assure you, as an
> >> introduction to OOP, it will blow away any non-CIS student.  It may
> >> also be difficult to modify, for example, if we want to do what
> >> Animals_2 does, and provide a custom display of characteristics for
> >> each class.
> >>
> >
> >Hmm, do you mean the talk method or the formatting of the inventory
> >data?  Because of the rather convoluted scoping rules you have to call
> >say animal_farm.<superclass name>.talk(self) to access the superclass
> >from within the talk method, which is a minor annoyance I guess.
> >
> >To format the show method differently for each class would be a little
> >more awkward, though you're not doing that at the moment.
> 
> I am intending to do exactly that.  Because the example is short, you
> are tempted to say I really don't need different show functions for
> each class.  I can add more unique items to each class if I must,
> making it look more like a "real program", or you can accept the
> requirement that the small differences in the example display are
> important.
> 

*Shrugs* fine, if you want that as a requirement.  I guess each class
could define a format function, and you could return the function
alongside the number.  You would still get the advantage of
inheritance.

> How about something like this:  We put all data and formatting unique
> to each class within that class, but we don't use the trick of each
> class calling its parent to generate a complete display.  The
> sequencing of displays could then be done by some generic function,
> calling in sequence the unique display functions from each class.
> 

Sounds reasonable.  I'd still be more comfortable if you used a
different example for your students, but it's up to you..

> >> One possibility is to make this an Animals_3 example.  Animals_1 was a
> >> simple two-class structure.  It served to introduce instance
> >> variables, and some basic concepts like inheritance.  When we moved to
> >> Animals_2, we pointed out the limitations of Animals_1, like not
> >> having enough classes to put variables like 'genus' where they really
> >> belong.
> >>
> >> Maybe we should go one more step, and make this a third example.  We
> >> can point out the limitations of Animals_2 in the introduction to
> >> Animals_3.  I can see the benefit of moving the print statements to
> >> the top level.  This is needed if we ever want to make the classes in
> >> Animals_2 work in some kind of framework with other classes.  The
> >> show() functions in Animals_2 could be modified to return a list of
> >> strings instead of printing directly to the console.
> >>
> >> I've posted your program as Solution 3 to the exercise at
> >> http://ece.arizona.edu/~edatools/Python/Exercises/  Could you give us
> >> a brief description of the advantages and disadvantages compared to
> >> the original.  I'm not able to do that, because I'm having difficulty
> >> restating what you have said above in terms that students will
> >> understand.  I cannot, for example, explain why your solution is more
> >> robust.
> >>
> >
> >Advantages:
> >    Robustness; no requirement for a programmer to keep a count of
> >    instances or update the count of the super class by calling it's
> >    initializer.  This minimises the chance of programmer error.
> >    Avoids replication of data, eliminating OOS errors.
> >
> >    Maintainability; all of the code for counting is contained in a
> >    single module, so modifications will not require changes to
> >multiple
> >    similar pieces of code throughout the class structure.
> >
> >    Agility; we can maintain more than one zoo in a program by
> >creating
> >    new instances of of the animal_farm class.  Class level data would
> >    limit us to data on a single zoo system.
> >
> >    Generality; the Collection class can be used for any problem of
> >    this type.
> 
> This is excellent.  I will add it to my web-page.
> 
> >> >Admittedly the solution is on the complicated side, though perhaps
> >> >someone with more experience than me could simplify things.
> >> >Unfortunately, a certain amount of complexity is just a reflection of
> >> >the fact that your demands strain the OO paradigm right to it's limit.
> >> > You could possibly implement the same thing in Java with a Factory
> >> >pattern, and perhaps the reflection API.
> >>
> >> Your vast experience may be blinding you to the problems non-CIS
> >> students will have with these more complex solutions.  I may be
> >> pushing a paradigm to some limit, but these are real-world problems
> >> that should be easily solved with a good OOP language.
> >
> >This isn't something I'd show a programming beginner either!  I'd
> >choose a different way of demonstrating a class heirarchy, myself.
> 
> OK, I'm convinced we need a third example.  Animals_2 will remain part
> of the introduction, and our next example Animals_3 will be our
> bullet-proof, production-quality,
> what-you-better-do-if-someone-is-paying-you, final solution.
> 
> I would still like to see some simplification of Animals_JM.py
> Although clarity is not specifically on your list of requirements, I
> think it does impact maintainability and programmer time.
> 

There are a couple of places where it could be simplified slightly,
but usually my approach to clarifying complex algorithms is just to
blit the code with comments.  I also try to avoid complexity by
picking a natural paradigm for the task, of course.

> Meanwhile, I'll try doing something with your "instances instead of
> classes" suggestion.
> 
> -- Dave



More information about the Python-list mailing list