By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
425,646 Members | 1,175 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 425,646 IT Pros & Developers. It's quick & easy.

Unification of Methods and Functions

P: n/a
I'm not getting any feedback on the most important benefit in my
proposed "Ideas for Python 3" thread - the unification of methods and
functions. Perhaps it was buried among too many other less important
changes, so in this thread I would like to focus on that issue alone.

I have edited the Proposed Syntax example below to take out the
changes unecessary to this discussion. I left in the change of
"instance variable" syntax ( self.sound --> .sound ) because that is
necessary for the unification of all method forms. ( Compare the
forms of the 'show' and 'talk' methods below.)

I believe these changes in syntax will make teaching OOP in Python
much easier. See "Prototypes.doc" at
http://ece.arizona.edu/~edatools/Python/ The first eight pages are a
basic, but complete presentation of the new OOP syntax. I expect this
to expand to about 30 pages with more examples and exercises. This
compares to about 60 pages in Learning Python 2nd ed.

If we measure complexity by the number of pages needed for a "textbook
explanation" of OOP, then I believe the new syntax has some real
benefits. At this point in the learning process, students already know
functions, modules, and global variables. The concept of using a
global variable __self__ is no surprise at all. The only thing new in
a class, compared to a module, is the instance variables, and they can
be explained in one paragraph. All methods look just like normal
functions. There is no need to explain "static methods" or any other
form of method. In fact, I use the term "function" rather than
"method" to emphasize the similarity.

I'm especially interested in feedback from users who are now learning
or have recently learned Python. I already know these changes seem
trivial to many experts. I've also heard plenty from the people who
think Python is so complex that we need to start a whole new language
( www.prothon.org ). I'm looking for a middle ground. I believe it
is possible to adopt what is good about Prothon, and not lose ten
years of software and community development.

======= Syntax Examples =============

## Proposed Syntax:
class Cat(Feline):
numCats = 0
def __init__( n = "unknown", s = "Meow" ):
Feline.__init__()
Cat.numCats += 1
.name = n # Set instance variables.
.sound = s
def show(): # Define a "static method".
Feline.show()
print " Cats:", Cat.numCats
def talk():
print "My name is ...", .name
print "I am a %s from %s" % (.genus, .home)
Mammal.talk() # Call an unbound function.
print __self__ ### Diagnostic check.

cat1 = Cat() # Create instance.
bf = cat1.talk # Make a bound function.
## Equivalent Python:
class Cat(Feline):
numCats = 0
def __init__(self, n = "unknown", s = "Meow" ):
Feline.__init__(self)
Cat.numCats += 1
self.name = n
self.sound = s
def show():
Feline.show()
print " Cats:", Cat.numCats
show = staticmethod(show)
def talk(self):
print "My name is ...", self.name
print "I am a %s from %s" % (self.genus, self.home)
Mammal.talk(self)
print self

cat1 = Cat() # Create instance.
bf = cat1.talk # Make a bound function.

========= End of Examples =======

Thanks for your help.

-- Dave

Jul 18 '05 #1
Share this Question
Share on Google+
99 Replies


P: n/a
David MacQuigg wrote:
[...]

Pardon my ignorance, but I'm not quite sure how this unificates methods
and functions at all.

In my eyes, unification would remove method definitions from the "class"
scope or make it simply syntactic sugar for:

def my_method(SomeClass self):
...

(instead of:
class SomeClass:
def my_method(self):
...)

This would also add the possibility to provide multiple dispatch by
simply allowing a "type hint" for every parameter at every position.

Cheers,
Michael
Jul 18 '05 #2

P: n/a
On Fri, Apr 30, 2004 at 09:47:05AM -0700, David MacQuigg wrote:
I'm not getting any feedback on the most important benefit in my
proposed "Ideas for Python 3" thread - the unification of methods and
functions. Perhaps it was buried among too many other less important
changes, so in this thread I would like to focus on that issue alone.
======= Syntax Examples =============

## Proposed Syntax:
class Cat(Feline):
numCats = 0
def __init__( n = "unknown", s = "Meow" ):
Feline.__init__()
Cat.numCats += 1
.name = n # Set instance variables.
.sound = s
def show(): # Define a "static method".
Feline.show()
print " Cats:", Cat.numCats
def talk():
print "My name is ...", .name
print "I am a %s from %s" % (.genus, .home)
Mammal.talk() # Call an unbound function.
print __self__ ### Diagnostic check.

cat1 = Cat() # Create instance.
bf = cat1.talk # Make a bound function.
## Equivalent Python:
class Cat(Feline):
numCats = 0
def __init__(self, n = "unknown", s = "Meow" ):
Feline.__init__(self)
Cat.numCats += 1
self.name = n
self.sound = s
def show():
Feline.show()
print " Cats:", Cat.numCats
show = staticmethod(show)
def talk(self):
print "My name is ...", self.name
print "I am a %s from %s" % (self.genus, self.home)
Mammal.talk(self)
print self

cat1 = Cat() # Create instance.
bf = cat1.talk # Make a bound function.

========= End of Examples =======


Explicit is better than implicit.
or
Magic BAAAAAAAAD [Phil Hartman as Frankenstein]

I suggest you check out perl to see mixing instance/class/static methods
in practice. Becuase perl is weakly typed this 'makes sense' in perl, even if
it causes problems[1]. What happens in practice is bad, people write functions
that can be used in two or more ways. This makes type checking hard, and
makes code unreadable. Functions frequently do slightly different things when
called one way or another.

For a python version you could do type checking on the function by doing
static analysis of the code, but that would be unpythonic. I like it when
a static function breaks badly when some yahoo tries to use self -- it breaks
early and loudly. Your way might only break on a certain code path that
tries to access '.name' and turns the method from static to instance.
If you re-added staticmethod/classmethod to clear up the distinction then
the above example just becomes a new syntax for implicit 'self'.

A version of the ':vars: expression' lambda replacement gets suggested every
so often (and that exact syntax once by me). It ain't going to happnen, labmda
is more likely to be dropped than enhanced.

-jackdied

[1] standard perl interview question, what is the difference between these
calls? All of these end up calling meow() with one argument, but they all
behave differently - sometimes very subtly.

$ob = new Cat;
$ob->meow(); # intsance method
Cat->meow(); # class method
Cat::meow($ob); # static method with first argument a Cat instance

Jul 18 '05 #3

P: n/a
David MacQuigg wrote:
I'm especially interested in feedback from users who are now learning
or have recently learned Python. I already know these changes seem
trivial to many experts. I've also heard plenty from the people who
think Python is so complex that we need to start a whole new language
( www.prothon.org ). I'm looking for a middle ground. I believe it
is possible to adopt what is good about Prothon, and not lose ten
years of software and community development.


I don't think Python is too complex and to me your suggestion adds more
complexity rather than simplying. Your proposal is also not
backward-compatible at all, so I don't see it can ever be accepted. I
agree that avoiding self make code lighter, but I don't think it unify
things or simplify them. It adds a magic and hidden parameter. The
good thing about the current syntax is that it shows what is really
happening. Actually, I find the statu quo more unified.

I would prefer self to be a keyword and be used to determine if the
function is static, but I guess we have to live with things like that.
I have given some python courses and the self parameter seems to be
clear for everybody. I think it even helps comprehension.

Regards,

Nicolas
Jul 18 '05 #4

P: n/a
On Fri, 30 Apr 2004 13:59:44 -0400, Jack Diederich
<ja**@performancedrivers.com> wrote:
On Fri, Apr 30, 2004 at 09:47:05AM -0700, David MacQuigg wrote:
I'm not getting any feedback on the most important benefit in my
proposed "Ideas for Python 3" thread - the unification of methods and
functions. Perhaps it was buried among too many other less important
changes, so in this thread I would like to focus on that issue alone.
======= Syntax Examples =============

## Proposed Syntax:
class Cat(Feline):
numCats = 0
def __init__( n = "unknown", s = "Meow" ):
Feline.__init__()
Cat.numCats += 1
.name = n # Set instance variables.
.sound = s
def show(): # Define a "static method".
Feline.show()
print " Cats:", Cat.numCats
def talk():
print "My name is ...", .name
print "I am a %s from %s" % (.genus, .home)
Mammal.talk() # Call an unbound function.
print __self__ ### Diagnostic check.

cat1 = Cat() # Create instance.
bf = cat1.talk # Make a bound function.
## Equivalent Python:
class Cat(Feline):
numCats = 0
def __init__(self, n = "unknown", s = "Meow" ):
Feline.__init__(self)
Cat.numCats += 1
self.name = n
self.sound = s
def show():
Feline.show()
print " Cats:", Cat.numCats
show = staticmethod(show)
def talk(self):
print "My name is ...", self.name
print "I am a %s from %s" % (self.genus, self.home)
Mammal.talk(self)
print self

cat1 = Cat() # Create instance.
bf = cat1.talk # Make a bound function.

========= End of Examples =======

Explicit is better than implicit.
or
Magic BAAAAAAAAD [Phil Hartman as Frankenstein]


I agree that magic is bad, but I disagree that the magically inserted
first argument on *some* function calls is less than the magic of
setting the current instance to a global variable __self__. Students
at this point already understand global variables. Yet they
frequently stumble on the different calling sequences for bound and
unbound functions. ( cat1.talk() vs Cat.talk(cat1) )

Using a global __self__ puts the current instance in a much more
convenient place, always available, but never in your way. This
"out-of-your-way" aspect of the proposed __self__ is the key to
getting a consistent calling sequence for all functions and methods.
If you want to call a method, but you don't have a current instance,
no problem. Just call it like any other function. We don't need
special syntax for a "static method".

It is hard to judge the complexity of a proposed syntax by looking
with a microscope at something as small as setting a global variable.
The way I would make the comparison is by looking at the length of a
"textbook" explanation of instance variables. I believe the proposed
syntax will take about half the number of lines to write a good
explanation. See the examples at the end of
http://ece.arizona.edu/~edatools/Pyt...typeSyntax.htm

There is a discussion of this question under the subject "Explanation
of Instance Variables in Python". Greg Ewing wrote a shorter
explanation than the one I prefer or the one that is in the Python
tutorial. I would like to get some more opinions, especially from
users who remember their first encounter with instance variables in
Python.
I suggest you check out perl to see mixing instance/class/static methods
in practice. Becuase perl is weakly typed this 'makes sense' in perl, even if
it causes problems[1]. What happens in practice is bad, people write functions
that can be used in two or more ways. This makes type checking hard, and
makes code unreadable. Functions frequently do slightly different things when
called one way or another.
You seem to be saying that there is a lot of confusion in Perl due to
mixing instance/class/static methods. I'm not familiar with Perl, but
this doesn't surprise me. It seems to *support* my proposal that we
do away with these variations in method calls. They serve no purpose
other than patching holes in the syntax which arose only because we
have this special first argument in *some* cases.
For a python version you could do type checking on the function by doing
static analysis of the code, but that would be unpythonic. I like it when
a static function breaks badly when some yahoo tries to use self -- it breaks
early and loudly. Your way might only break on a certain code path that
tries to access '.name' and turns the method from static to instance.
If you re-added staticmethod/classmethod to clear up the distinction then
the above example just becomes a new syntax for implicit 'self'.
I'm not following this. In the proposed syntax, there is no
distinction between static and class or instance methods. All methods
are called the same way. If you call a method that has instance
variables, it will look for __self__ to resolve those variables. If
__self__ is set to None, or to some inappropriate type, you will get
the same error message you get now from Python:
TypeError: unbound method talk() must be called with Cat instance as
__self__ (got Mammal instance instead)
A version of the ':vars: expression' lambda replacement gets suggested every
so often (and that exact syntax once by me). It ain't going to happnen, labmda
is more likely to be dropped than enhanced.
I think there is some value in having an anonymous function syntax,
not enough to justify a keyword, but certainly enough if it can be
done with a standard function definition, just leaving off the name.
Here is my latest favorite function definition syntax:

f(x):
return x**2

Lambda form:
:x:x**2

The lambda form is close enough to the standard form that it is
self-explanatory. So we can drop three more pages from the standard
introductory text.

-jackdied

[1] standard perl interview question, what is the difference between these
calls? All of these end up calling meow() with one argument, but they all
behave differently - sometimes very subtly.

$ob = new Cat;
$ob->meow(); # intsance method
Cat->meow(); # class method
Cat::meow($ob); # static method with first argument a Cat instance


The equivalent question in Python is -- explain the difference in
these uses of the talk method:

cat1.talk()
Mammal.talk(cat1)
bound_func = cat1.talk
unbound_func = Mammal.talk

This is a clear violation of the "explicit" rule. Yet it is one that
is justified, and one that I decided to keep in the prposed syntax.
The alternative is a special syntax to provide explicit binding in all
situations. After weeks of debate on the Prothon list, we were not
able to come up with anything better than Python's syntax.

So I have decided to accept the simple implicit rule -- you get a
bound function from an instance and the equivalent unbound function
from its parent class. Practicality beats purity.

-- Dave

Jul 18 '05 #5

P: n/a
David MacQuigg <dm*@gain.com> wrote in message news:<4a********************************@4ax.com>. ..
On 7 May 2004 06:31:51 -0700, mo******@tcd.ie (James Moughan) wrote:

Let me give an example:

def getLength(s): return s.length

class Foo:
length = 5

Foo.getLength = getLength

foo = Foo()
print length(foo), foo.length()
The last two function calls don't work. There is no such function
named 'length' and foo.length is an integer, not a function.


Ooops, let me re-write it how I meant it :)
def getLength(s): return s.length

class Foo:
length = 5

Foo.getLength = getLength

foo = Foo()
print getLength(foo), Foo.getLength(foo), foo.getLength()

Obviously I meant getLength, not length, on the last line.
The example in the proposed syntax would be the same, except there
would be no 'self' in the function definition, and there would be no
magic first argument 'foo' in the last call.
foo isn't a 'magic argument'; I'm calling a function with a parameter,
which happens to be an object. This is normal. The example stresses
that methods are functions which just happen to be attached to a
class.
Also, if you are calling
a function that has an instance variable ( .length ) and no instance
has been set by a prior binding, you would need to set __self__
manually.
__self__ = foo; print FooLen()
???!!!???

This is what I was talking about in my first post, global variables
which change depending on where you are in the code... as I understand
what you're saying, __self__ will have to be set, then reset when a
method is called from within a method and the exits. And __self__
could presumably be changed halfway through a method, too. I'm sorry,
I don't see this as being more explicit or simpler.

This is rarely needed. Normally a call to an unbound function would
be in a context where __self__ is already set. From the Animals_2.py
example: cat1.talk() calls Cat.talk() which calls Mammal.talk()
__self__ is set to cat1 on the first call, and it is not changed by
the call to the unbound function Mammal.talk()

A method in a class in Python is just like a global function; for a
global function to operate on an object, it must take it as an
argument. The prototype syntax would appear to break the above
example.
Global functions have no instance variables, so there is no need for a
special first argument. A Python method requires a special first
argument (even if it is not used).


But the first argument isn't terribly 'special'; it tells the method
what it's working on, just like any other argument. It's only
'special' characteristic is that there's some syntactic sugar to
convert foo.getLength() into Foo.getLength(foo).
The difference in the proposed syntax is that it doesn't need the
staticmethod wrapper to tell the interpreter -- don't expect a special
first argument. In the new syntax all functions/methods will have the
same calling sequence.


If a method doesn't operate on the data from an object then as a rule
it should be global. There are exceptions, but they generally don't
occur in Python so much as a in 'true oo' language like Java.


The placement of a function at the module level or in a class should
be determined by the nature of the function, not any syntax problems.
If the function has characteristics unique to a class, it ought to be
included with that class. The Mammal.show() function, for example,
provides a display of characteristics unique to mammals, so we put it
in class Mammal. We could have written a general-purpose Inventory()
function to recursively walk an arbitrary class hierarchy and print
the number of instances of each class. That general function would be
best placed at the global level, outside of any one class.


Mammal.show() shows characteristics to do with Mammals, *but not
specifically Mammal*. There really is a difference between a class
and it's subclasses.

The general-purpose inventory solution would be a better solution. It
doesn't require repetition, it's hard (impossible?) to break and it's
generic, allowing it to be used beyond this single class heirarchy.

If the inventory function would be best placed outside a class, why do
you think it's a good idea to put something with exactly the same
functionality inside your classes?
I've looked at a few introductions to Python, and in my opinion
Learning Python, 2nd ed, by Mark Lutz and David Ascher is the best.
It strikes a good balance between the minimal presentation that
tersely covers the essentials for an experienced programmer vs the
long, windy introductions that put you to sleep with analogies to car
parts and other "objects". Lutz takes 95 pages to cover OOP. I think
I could do a little better, maybe 70 pages, but that may be just my
ego :>)

When you say ten pages, you must be making some very different
assumptions about the students or their prior background, or the
desired level of proficiency. The least I can imagine is about 30
pages, if we include exercises and examples. And that 30 assumes we
get rid of all the unneccesary complexity (static methods, lambdas,
etc.) that currently fills the pages of Learning Python.


I'm assuming they alreay know the general structures of programming in
Python, and that you can then just show them how to package data and
methods into a class with a clear example, by rewriting a program
you've shown them before. After that it's mainly a question of
explaining why you should do it, which is probably rather more
important than how.


*Why* is not an issue with my students. They will have plenty of
examples in our circuit design program to look at. If anyone ever
asks "Why OOP", I'll just have them look at the Qt Toolkit, which will
be the basis of our user interface. Enormous complexity, packaged in
a very easy-to-use set of objects.


Well, I don't know your course so I guess I shouldn't comment on what
you're doing. :)
I need to show them *how* to undestand the OOP structures in that
program, and do it in the most efficient way. The chapter on
Prototypes at http://ece.arizona.edu/~edatools/Python/ is my best
effort so far. I've got the basic explanation down to 10 pages. I
expect this to expand to about 30 with examples and exercises. That
will be about four hours of additional work, including lecture,
reading, and practice.
I've never met anyone who had difficulty in understanding anything
about the syntax of OO other than the class/object distinction. It's
fundamentally very simple once you have a basis in all the other
programming techniques.
I guess you haven't met anyone learning C++. :>)


Lol, not who hadn't taken up Java beforehand, no. C++ does a
wonderful job of obscuring a simple language behind crazy syntax.
Unless you're talking about the entire programming course, 70 pages is
waaay too much - your students just will not read them, regardless of
how brilliant they are.


Learning Python, 2nd ed. by Mark Lutz and David Ascher is generally
considered the best introductory text on Python. 96 pages on OOP.


Books are always kind of strange, because a book must have a certain
number of pages and cover a certain range of content at a certain
technical level. For the level and range of the ORA Learning books,
that is going to mean a bit of padding for a simple language like
Python. If I see Learning Python in a bookshop then I'll take a look,
though.

Regardless, I stand by what I said before - students generally will
not read 70 pages on a single topic, especially when it's a relatively
minor part of the course.
[snip a few paragraphs where we agree !! :>) ]
Learning to program is about 5% how to do something, and 95% when and
why you should do it. You seem to be focusing almost exclusively on
how, which I suspect is why we're all so upset :) you get that way
when you have to fix the code which eventually results.


The OOP presentations I've seen that focus as much as 50% on *why*
generally leave me bored and frustrated. I feel like screaming --
Stop talking about car parts and show me some nice code examples. If
it's useful, I'm motivated. Good style is a separate issue, also best
taught with good examples (and some bad for contrast).


I'm not talking about car parts. I'm talking about explaining
modularity, complexity, side-effects, classes as data structures etc.

(It's hilarious to see what happens when people get taught by car-part
style metaphors; they take them completely literally. I've seen
someone writing the classic vending machine example write a 'Can'
class, subclass it to get 'CokeCan', 'PepsiCan'... and then create ten
of each to represent the machines' stock. That was after three years
of university, too...)

OK: "The whole idea of having these structures in any program is
wrong."

Firstly, the program uses a class hierarchy as a data structure. That
isn't what class heirarchies are designed for, and not how they should
be used IMO. But it's what any bright student will pick up from the
example.


The classes contain both data and functions. The data is specific to
each class. I even show an example of where the two-class first
example forced us to put some data at an inappropriate level, but with
a four class hierarchy, we can put each data item right where it
belongs.


The data is not specific to the class. It's specific to the class and
it's subclasses. Subclasses should be dependent on the superclass,
and generally not the other way around.

Nothing in the Bovine class can affect anything in a Cat. Feline and
Bovine are independent branches below Mammal. Adding a Mouse class
anywhere other than in the chain Cat - Feline - Mammal - Animal cannot
affect Cat. Could you give a specific example?

Say someone adds a mouse class but doesn't call the constructor for
Mammal. The data produced by mammal and therefore cat is now
incorrect, as instances of mouse are not included in your count. In a
real example, anything might be hanging on that variable - so e.g.
someone adds some mouse instances and the program crashes with an
array index out of bounds (or whatever the Pythonic equivalent is :) )
, or maybe we just get bad user output. This type of behaviour is
damn-near impossible to debug in a complex program, because you didn't
change anything which could have caused it. It's caused by what you
didn't do.
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.
Chaining them together results in a
sequence of calls, and a sequence of outputs that is exactly what we
want. The nice thing about separating the total "show" functionality
into parts specific to each class is that when we add a class in the
middle, as I did with Feline, inserted between Mammal and Cat, it is
real easy to change the Cat class to accomodate the insertion.

Python has a 'super' function to facilitate this kind of chaining.
Michele Simionato's 'prototype.py' module makes 'super' even easier to
use. Instead of having Cat.show() call Mammal.show() I can now just
say super.show() and it will automatically call the show() function
from whatever class is the current parent. Then when I add a Feline
class between Mammal and Cat, I don't even need to change the
internals of Cat.
That's fine - providing you're not using a class heirarchy to store
data. It's not the act of calling a method in a super-class which is
a bad idea, it's the way you are making *the numbers outputted* from
cat dependent of actions taken *or not taken* in another class
*completely outside cat's scope*.
What I'm looking for is not clever re-structuring, but just a
straightforward translation, and some comments along the way -- oh
yes, that is a little awkward having to use a staticmethod here. Wow,
you mean staticmethods aren't fundamentally necessary, just a bandaid
to make up for Python's deficiencies? That was my reaction when I
first saw Prothon.


Static methods are more like a band-aid to make up for the
deficiencies of OOP. Python isn't a pure OO language, and doesn't
suffer the need for them badly.


In one syntax we need special "static methods" to handle calls where a
specific instance is not available, or not appropriate. In another
syntax we can do the same thing with one universal function form.

-- Dave

Jul 18 '05 #6

P: n/a
On 8 May 2004 07:07:09 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<4a********************************@4ax.com>. ..
On 7 May 2004 06:31:51 -0700, mo******@tcd.ie (James Moughan) wrote: <snip>
Also, if you are calling
a function that has an instance variable ( .length ) and no instance
has been set by a prior binding, you would need to set __self__
manually.
__self__ = foo; print FooLen()
???!!!???

This is what I was talking about in my first post, global variables
which change depending on where you are in the code... as I understand
what you're saying, __self__ will have to be set, then reset when a
method is called from within a method and the exits. And __self__
could presumably be changed halfway through a method, too. I'm sorry,
I don't see this as being more explicit or simpler.


The setting of __self__ happens automatically, just like the setting
of the first argument in a call from an instance. The user doesn't
have to worry about it. In fact, I can't think of a circumstance
where the user would need to explicitly set __self__. Maybe some
diagnostic code, in which case having available a system variable like
__self__ is a plus. You can, without any loss of functionality in a
normal program, never mention __self__ in an introductory course. The
user doesn't need to know what it is called.

My preference is to give it a name and highlight it with double
underscores. To me that makes the discussion more concrete and
explicit, and builds on concepts already understood. Don't forget,
the students already understand global variables at this point in the
course. The "magic" of setting a particular global variable to an
instance is about the same as the magic of inserting that instance as
a first argument in a function call. The problem in either syntax is
not the magic of setting 'self' or '__self__'.

<snip>
>A method in a class in Python is just like a global function; for a
>global function to operate on an object, it must take it as an
>argument. The prototype syntax would appear to break the above
>example.


Global functions have no instance variables, so there is no need for a
special first argument. A Python method requires a special first
argument (even if it is not used).


But the first argument isn't terribly 'special'; it tells the method
what it's working on, just like any other argument. It's only
'special' characteristic is that there's some syntactic sugar to
convert foo.getLength() into Foo.getLength(foo).


The specialness of the first argument isn't much, I agree, but it is
enough to make the calling sequence different from a normal function
or a static method. It is these differences that the new syntax gets
rid of, thereby enabling the unification of all methods and functions,
and simplifying the presentation of OOP. Methods in the new syntax
are identical to functions (which the students already understand),
except for the presence of instance variables.

Instance variables are the one fundamental difference between
functions and methods, and one that we wish to focus our entire
attention on in the presentation. Any new and unnecessary syntactic
clutter is a distraction, particularly if the new syntax is used in
some cases (normal methods) but not others (static methods).
>> The difference in the proposed syntax is that it doesn't need the
>> staticmethod wrapper to tell the interpreter -- don't expect a special
>> first argument. In the new syntax all functions/methods will have the
>> same calling sequence.
>
>If a method doesn't operate on the data from an object then as a rule
>it should be global. There are exceptions, but they generally don't
>occur in Python so much as a in 'true oo' language like Java.


The placement of a function at the module level or in a class should
be determined by the nature of the function, not any syntax problems.
If the function has characteristics unique to a class, it ought to be
included with that class. The Mammal.show() function, for example,
provides a display of characteristics unique to mammals, so we put it
in class Mammal. We could have written a general-purpose Inventory()
function to recursively walk an arbitrary class hierarchy and print
the number of instances of each class. That general function would be
best placed at the global level, outside of any one class.


Mammal.show() shows characteristics to do with Mammals, *but not
specifically Mammal*. There really is a difference between a class
and it's subclasses.


The Mammal.show() function *is* specific to Mammal. I think what you
are saying is that calling Mammal.show() results in a display of
characteristics of both Mammal and its ancestor Animal. That is a
requirement of the problem we are solving, not a result of bad
programming. We want to see *all* the characteristics of Mammal,
including those it inherited from Animal.

Leave out the call to Animal.show() if you don't want to also see the
ancestor's data.
The general-purpose inventory solution would be a better solution. It
doesn't require repetition, it's hard (impossible?) to break and it's
generic, allowing it to be used beyond this single class heirarchy.

If the inventory function would be best placed outside a class, why do
you think it's a good idea to put something with exactly the same
functionality inside your classes?
The proposed Inventory() function is a general function that *would*
be appropriate outside a class. The exising class-specific functions
like Mammal.show() are unique to each class. I tried to make that
clear in a short example by giving each data item a different text
label. I've now added some unique data to the example just so we can
get past this stumbling block. A real program would have a multi-line
display for each class, and there would be *no way* you could come up
with some general function to produce that display for any class.

<snip>
Learning Python, 2nd ed. by Mark Lutz and David Ascher is generally
considered the best introductory text on Python. 96 pages on OOP.


Books are always kind of strange, because a book must have a certain
number of pages and cover a certain range of content at a certain
technical level. For the level and range of the ORA Learning books,
that is going to mean a bit of padding for a simple language like
Python. If I see Learning Python in a bookshop then I'll take a look,
though.

Regardless, I stand by what I said before - students generally will
not read 70 pages on a single topic, especially when it's a relatively
minor part of the course.


Learning Python, 2nd ed. would be appropriate for a one-semester
course. My problem is that I have only a fraction of a semester in a
circuit-design course. So I don't cover OOP at all. I would include
OOP if I could do it with four more hours. Currently Python is a
little over the top. I don't think it is a problem with Lutz's book.
He covers what he needs to, and at an appropriate pace.
>Learning to program is about 5% how to do something, and 95% when and
>why you should do it. You seem to be focusing almost exclusively on
>how, which I suspect is why we're all so upset :) you get that way
>when you have to fix the code which eventually results.


The OOP presentations I've seen that focus as much as 50% on *why*
generally leave me bored and frustrated. I feel like screaming --
Stop talking about car parts and show me some nice code examples. If
it's useful, I'm motivated. Good style is a separate issue, also best
taught with good examples (and some bad for contrast).


I'm not talking about car parts. I'm talking about explaining
modularity, complexity, side-effects, classes as data structures etc.


These are concepts that design engineers understand very well. I
wouldn't spend any time teaching them about modularity, but I would
point out how different program structures facilitate modular design,
and how syntax can sometimes restrict your ability to modularize as
you see fit. Case in point: The need for static methods to put the
show() functions where we want them.
(It's hilarious to see what happens when people get taught by car-part
style metaphors; they take them completely literally. I've seen
someone writing the classic vending machine example write a 'Can'
class, subclass it to get 'CokeCan', 'PepsiCan'... and then create ten
of each to represent the machines' stock. That was after three years
of university, too...)


Oh ... don't get me started on academia. :>)
>OK: "The whole idea of having these structures in any program is
>wrong."
>
>Firstly, the program uses a class hierarchy as a data structure. That
>isn't what class heirarchies are designed for, and not how they should
>be used IMO. But it's what any bright student will pick up from the
>example.


The classes contain both data and functions. The data is specific to
each class. I even show an example of where the two-class first
example forced us to put some data at an inappropriate level, but with
a four class hierarchy, we can put each data item right where it
belongs.


The data is not specific to the class. It's specific to the class and
it's subclasses. Subclasses should be dependent on the superclass,
and generally not the other way around.


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.
Nothing in the Bovine class can affect anything in a Cat. Feline and
Bovine are independent branches below Mammal. Adding a Mouse class
anywhere other than in the chain Cat - Feline - Mammal - Animal cannot
affect Cat. Could you give a specific example?


Say someone adds a mouse class but doesn't call the constructor for
Mammal. The data produced by mammal and therefore cat is now
incorrect, as instances of mouse are not included in your count. In a
real example, anything might be hanging on that variable - so e.g.
someone adds some mouse instances and the program crashes with an
array index out of bounds (or whatever the Pythonic equivalent is :) )
, or maybe we just get bad user output. This type of behaviour is
damn-near impossible to debug in a complex program, because you didn't
change anything which could have caused it. It's caused by what you
didn't do.


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.
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.
Chaining them together results in a
sequence of calls, and a sequence of outputs that is exactly what we
want. The nice thing about separating the total "show" functionality
into parts specific to each class is that when we add a class in the
middle, as I did with Feline, inserted between Mammal and Cat, it is
real easy to change the Cat class to accomodate the insertion.

Python has a 'super' function to facilitate this kind of chaining.
Michele Simionato's 'prototype.py' module makes 'super' even easier to
use. Instead of having Cat.show() call Mammal.show() I can now just
say super.show() and it will automatically call the show() function
from whatever class is the current parent. Then when I add a Feline
class between Mammal and Cat, I don't even need to change the
internals of Cat.


That's fine - providing you're not using a class heirarchy to store
data. It's not the act of calling a method in a super-class which is
a bad idea, it's the way you are making *the numbers outputted* from
cat dependent of actions taken *or not taken* in another class
*completely outside cat's scope*.


Seems like this is the way it has to be if you want to increment the
counts for Cat and all its ancestors whenever you create a new
instance of Cat. Again, I'm not understanding the problem you are
seeing. You seem to be saying there should be only methods, not data,
stored in each class.
>> What I'm looking for is not clever re-structuring, but just a
>> straightforward translation, and some comments along the way -- oh
>> yes, that is a little awkward having to use a staticmethod here. Wow,
>> you mean staticmethods aren't fundamentally necessary, just a bandaid
>> to make up for Python's deficiencies? That was my reaction when I
>> first saw Prothon.
>
>Static methods are more like a band-aid to make up for the
>deficiencies of OOP. Python isn't a pure OO language, and doesn't
>suffer the need for them badly.


In one syntax we need special "static methods" to handle calls where a
specific instance is not available, or not appropriate. In another
syntax we can do the same thing with one universal function form.


To try and get to the bottom of this, I re-wrote the Animals.py
example, following what I think are your recommendations on moving the
static methods to module-level functions. I did not move the data out
of the classes, because that makes no sense to me at all.

Take a look at http://ece.arizona.edu/~edatools/Python/Exercises/ and
let me know if Animals_2b.py is what you had in mind. If not, can you
edit it to show me what you mean?

-- Dave

Jul 18 '05 #7

P: n/a
David MacQuigg wrote:
In Python, staticmethods are not fundamentally necessary,
full stop. We keep trying to tell you that, but it seems
you have your fingers in your ears.


I will stop this subthread right here unless you change your tone.


Sorry, I take that back.

I stand by the point I was trying to make, though. You
don't need to use static methods at all in a language,
like Python, which doesn't force you to make all functions
a method of something.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #8

P: n/a
On Mon, 10 May 2004 15:08:26 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
David MacQuigg wrote:
In Python, staticmethods are not fundamentally necessary,
full stop. We keep trying to tell you that, but it seems
you have your fingers in your ears.


I will stop this subthread right here unless you change your tone.


Sorry, I take that back.

I stand by the point I was trying to make, though. You
don't need to use static methods at all in a language,
like Python, which doesn't force you to make all functions
a method of something.


Apology accepted. You are welcome to participate in the discussion of
the best way to structure this introductory Animals.py example. I'm
getting some suggetions from James Moughan, and I put t

Jul 18 '05 #9

P: n/a
On Mon, 10 May 2004 15:08:26 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
David MacQuigg wrote:
In Python, staticmethods are not fundamentally necessary,
full stop. We keep trying to tell you that, but it seems
you have your fingers in your ears.


I will stop this subthread right here unless you change your tone.


Sorry, I take that back.

I stand by the point I was trying to make, though. You
don't need to use static methods at all in a language,
like Python, which doesn't force you to make all functions
a method of something.


Apology accepted. You are welcome to participate in the discussion of
the best way to structure this introductory Animals.py example. See
the discussion with James Moughan in another part of this thread.

I decided to write this as an exercise for the class. See
http://ece.arizona.edu/~edatools/Python/Exercises

Your suggestions will be appreciated.

-- Dave


Jul 18 '05 #10

P: n/a
David MacQuigg <dm*@gain.com> wrote in message news:<88********************************@4ax.com>. ..
On 8 May 2004 07:07:09 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<4a********************************@4ax.com>. ..
On 7 May 2004 06:31:51 -0700, mo******@tcd.ie (James Moughan) wrote: <snip> Also, if you are calling
a function that has an instance variable ( .length ) and no instance
has been set by a prior binding, you would need to set __self__
manually.
__self__ = foo; print FooLen()
???!!!???

This is what I was talking about in my first post, global variables
which change depending on where you are in the code... as I understand
what you're saying, __self__ will have to be set, then reset when a
method is called from within a method and the exits. And __self__
could presumably be changed halfway through a method, too. I'm sorry,
I don't see this as being more explicit or simpler.


The setting of __self__ happens automatically, just like the setting
of the first argument in a call from an instance. The user doesn't
have to worry about it.


Explicit is better than implicit, especially in getting people to
understand things. :)
In fact, I can't think of a circumstance
where the user would need to explicitly set __self__. Maybe some
diagnostic code, in which case having available a system variable like
__self__ is a plus. You can, without any loss of functionality in a
normal program, never mention __self__ in an introductory course. The
user doesn't need to know what it is called.

I can think of a time where I'd both want to do it and couldn't;

may(str.strip, list_of_str)

See what I mean about it breaking the funtional side of things?
Without some definition about what's a static method the interpreter
can't resolve things like this. In practice, map and a whole bunch of
functions would have to be modified to explicitly set __self__.

An implicit self is what leads to the whole
mem_fun(&std::vector<int>::push_back) (or whatever the exact syntax
is, it's too unusable to bother learning) in c++.

My preference is to give it a name and highlight it with double
underscores. To me that makes the discussion more concrete and
explicit, and builds on concepts already understood. Don't forget,
the students already understand global variables at this point in the
course. The "magic" of setting a particular global variable to an
instance is about the same as the magic of inserting that instance as
a first argument in a function call. The problem in either syntax is
not the magic of setting 'self' or '__self__'.

<snip>
>A method in a class in Python is just like a global function; for a
>global function to operate on an object, it must take it as an
>argument. The prototype syntax would appear to break the above
>example.

Global functions have no instance variables, so there is no need for a
special first argument. A Python method requires a special first
argument (even if it is not used).
But the first argument isn't terribly 'special'; it tells the method
what it's working on, just like any other argument. It's only
'special' characteristic is that there's some syntactic sugar to
convert foo.getLength() into Foo.getLength(foo).


The specialness of the first argument isn't much, I agree, but it is
enough to make the calling sequence different from a normal function
or a static method. It is these differences that the new syntax gets
rid of, thereby enabling the unification of all methods and functions,
and simplifying the presentation of OOP. Methods in the new syntax
are identical to functions (which the students already understand),
except for the presence of instance variables.

Instance variables are the one fundamental difference between
functions and methods, and one that we wish to focus our entire
attention on in the presentation.


Except that that distinction doesn't exist in Python, since calling an
instance variable is an explicit call to a member of an instance. If
you are trying to focus your presentation on something which doesn't
exist in Python then things are naturally going to be awkward. I
would suggest that it's not a problem with the language, though. :)
Any new and unnecessary syntactic
clutter is a distraction, particularly if the new syntax is used in
some cases (normal methods) but not others (static methods).

If you really want to get something like this accepted then the
closest thing which *might* have a chance would be to redefine the
calling sequence of some_class.method() so that it doesn't seek a self
argument so long as ``method'' was defined without one. I don't think
this would break anyone's code (I may well be wrong of course) since
the sequence isn't currently valid in execution (though IIRC it will
compile to bytecode).

This makes the distinction between static and normal methods about as
simple as it can be; methods are just class methods which operate on
an instance. You can show how it works in a few lines of an example
(though, currently, it should only take a few more.)

Mammal.show() shows characteristics to do with Mammals, *but not
specifically Mammal*. There really is a difference between a class
and it's subclasses.


The Mammal.show() function *is* specific to Mammal. I think what you
are saying is that calling Mammal.show() results in a display of
characteristics of both Mammal and its ancestor Animal.


No, it's not. Let me try to be totally clear here; The numMammals
data member contains data not just about Mammal, but also about
instances of it's subclasses. This is the problem. The fact that
it's accessed through the show method is really just a detail, though
the presence of of show in other subclasses compounds the problem.
That is a
requirement of the problem we are solving, not a result of bad
programming. We want to see *all* the characteristics of Mammal,
including those it inherited from Animal.

You are not solving a problem; that's the problem. :) If there were a
real programming task then it would be more trivial to show why your
object model is broken.
Leave out the call to Animal.show() if you don't want to also see the
ancestor's data.
The general-purpose inventory solution would be a better solution. It
doesn't require repetition, it's hard (impossible?) to break and it's
generic, allowing it to be used beyond this single class heirarchy.

If the inventory function would be best placed outside a class, why do
you think it's a good idea to put something with exactly the same
functionality inside your classes?
The proposed Inventory() function is a general function that *would*
be appropriate outside a class. The exising class-specific functions
like Mammal.show() are unique to each class. I tried to make that
clear in a short example by giving each data item a different text
label. I've now added some unique data to the example just so we can
get past this stumbling block. A real program would have a multi-line
display for each class, and there would be *no way* you could come up
with some general function to produce that display for any class.


Books are always kind of strange, because a book must have a certain
number of pages and cover a certain range of content at a certain
technical level. For the level and range of the ORA Learning books,
that is going to mean a bit of padding for a simple language like
Python. If I see Learning Python in a bookshop then I'll take a look,
though.

Regardless, I stand by what I said before - students generally will
not read 70 pages on a single topic, especially when it's a relatively
minor part of the course.


Learning Python, 2nd ed. would be appropriate for a one-semester
course. My problem is that I have only a fraction of a semester in a
circuit-design course. So I don't cover OOP at all. I would include
OOP if I could do it with four more hours. Currently Python is a
little over the top. I don't think it is a problem with Lutz's book.
He covers what he needs to, and at an appropriate pace.


If you can't take it below 70 pages and you only have 4 hours... maybe
it's not such a great idea to try this? I can't see your students
benefiting from what you're proposing to do, if you have so little
time.
>Learning to program is about 5% how to do something, and 95% when and
>why you should do it. You seem to be focusing almost exclusively on
>how, which I suspect is why we're all so upset :) you get that way
>when you have to fix the code which eventually results.

The OOP presentations I've seen that focus as much as 50% on *why*
generally leave me bored and frustrated. I feel like screaming --
Stop talking about car parts and show me some nice code examples. If
it's useful, I'm motivated. Good style is a separate issue, also best
taught with good examples (and some bad for contrast).


I'm not talking about car parts. I'm talking about explaining
modularity, complexity, side-effects, classes as data structures etc.


These are concepts that design engineers understand very well. I
wouldn't spend any time teaching them about modularity, but I would
point out how different program structures facilitate modular design,
and how syntax can sometimes restrict your ability to modularize as
you see fit. Case in point: The need for static methods to put the
show() functions where we want them.
>OK: "The whole idea of having these structures in any program is
>wrong."
>
>Firstly, the program uses a class hierarchy as a data structure. That
>isn't what class heirarchies are designed for, and not how they should
>be used IMO. But it's what any bright student will pick up from the
>example.

The classes contain both data and functions. The data is specific to
each class. I even show an example of where the two-class first
example forced us to put some data at an inappropriate level, but with
a four class hierarchy, we can put each data item right where it
belongs.


The data is not specific to the class. It's specific to the class and
it's subclasses. Subclasses should be dependent on the superclass,
and generally not the other way around.


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.
Nothing in the Bovine class can affect anything in a Cat. Feline and
Bovine are independent branches below Mammal. Adding a Mouse class
anywhere other than in the chain Cat - Feline - Mammal - Animal cannot
affect Cat. Could you give a specific example?

Say someone adds a mouse class but doesn't call the constructor for
Mammal. The data produced by mammal and therefore cat is now
incorrect, as instances of mouse are not included in your count. In a
real example, anything might be hanging on that variable - so e.g.
someone adds some mouse instances and the program crashes with an
array index out of bounds (or whatever the Pythonic equivalent is :) )
, or maybe we just get bad user output. This type of behaviour is
damn-near impossible to debug in a complex program, because you didn't
change anything which could have caused it. It's caused by what you
didn't do.


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.

While errors are always going to happen, OOP calls on some conventions
to minimize them. The most absolutely vital of these is that it's
clear what can break what. Generally I should never be able to break
a subsystem by breaking it's wrapper; definitely I should never be
able to break a superclass by breaking it's subclass; and I
*certainly* shouldn't be able to break a part of the system by
changing something unconnected to it. The whole of OOP derives, more
or less directly, from these principles. Expressions like 'A is a
part/type of B' derive from this philosophy, not the other way around.

Your program breaks with this concept. It allows an event in Cat to
affect data in Mammal and in Animal, which also has knock-on effects
for every other subclass of these. Therefore it is bad object
oriented programming.

It takes us back to the days before even structured programming, when
no-one ever had any idea what the effects of altering or adding a
piece of code would be.

It is therefore not a good teaching example. :)

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.
Chaining them together results in a
sequence of calls, and a sequence of outputs that is exactly what we
want. The nice thing about separating the total "show" functionality
into parts specific to each class is that when we add a class in the
middle, as I did with Feline, inserted between Mammal and Cat, it is
real easy to change the Cat class to accomodate the insertion.

Python has a 'super' function to facilitate this kind of chaining.
Michele Simionato's 'prototype.py' module makes 'super' even easier to
use. Instead of having Cat.show() call Mammal.show() I can now just
say super.show() and it will automatically call the show() function
from whatever class is the current parent. Then when I add a Feline
class between Mammal and Cat, I don't even need to change the
internals of Cat.


That's fine - providing you're not using a class heirarchy to store
data. It's not the act of calling a method in a super-class which is
a bad idea, it's the way you are making *the numbers outputted* from
cat dependent of actions taken *or not taken* in another class
*completely outside cat's scope*.


Seems like this is the way it has to be if you want to increment the
counts for Cat and all its ancestors whenever you create a new
instance of Cat. Again, I'm not understanding the problem you are
seeing. You seem to be saying there should be only methods, not data,
stored in each class.


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.

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.
In one syntax we need special "static methods" to handle calls where a
specific instance is not available, or not appropriate. In another
syntax we can do the same thing with one universal function form.
To try and get to the bottom of this, I re-wrote the Animals.py
example, following what I think are your recommendations on moving the
static methods to module-level functions. I did not move the data out
of the classes, because that makes no sense to me at all.


*Sigh* No, I must say that doesn't help much. :-\

As I said, there is something wrong with the whole idea behind it; the
design needs refactoring, not individual lines of code.

Having said that, I'll try to redact the issues as best I can, on the
basis that it may illustrate what I mean.

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.

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.

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.

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

class Collective:
class base: pass

def startup(self, coll, root):
#wrapper class to count creations of classes
self.root = root
class wrapper:
def __init__(self, name, c):
self.mycount = 0
self.c = c
self.name = name
def __call__(self, *arg):
tmp = self.c(*arg)
self.mycount += 1
return self.c(*arg)
self.wrapper = wrapper
#replace every class derived from root with a wrapper
#plus build a table of the
self.wrap_list = []
for name, elem in coll.__dict__.items():
try:
if issubclass(elem, self.root):
tmp = wrapper(name, elem)
self.__dict__[name] = tmp
self.wrap_list.append(tmp)
except: pass

#when subclassing, override this
#call startup with the class name
#and the root of the class heirarchy
def __init__(self):
self.startup(Collective, self.base)

#here's the stuff to do the counting
#this could be much faster with marginally more work
#exercise for the reader... ;)

def get_counts(self, klass):
counts = [ (x.c, (self.get_sub_count(x), x.name)) \
for x in self.super_classes(klass) ]
counts.append( (klass.c, (self.get_sub_count(klass),
klass.name)) )
counts.sort(lambda x, y: issubclass(x[0], y[0]))
return [x[-1] for x in counts]

def get_sub_count(self, klass):
count = klass.mycount
for sub in self.sub_classes(klass):
count += sub.mycount
return count
def super_classes(self, klass):
return [x for x in self.wrap_list if issubclass(klass.c, x.c)
\
and not x.c is klass.c]
def sub_classes(self, klass):
return [x for x in self.wrap_list if issubclass(x.c, klass.c)
\
and not x.c is klass.c]

So we can now do:

class animal_farm(Collective):
class Animal: pass
class Mammal(Animal): pass
class Bovine(Mammal): pass
class Feline(Mammal): pass
class Cat(Feline): pass
def __init__(self):
self.startup(animal_farm, self.Animal)
a_farm = animal_farm()
cat = a_farm.Cat()
feline = a_farm.Mammal()
print a_farm.get_counts(a_farm.Feline)
[(2, 'Animal'), (2, 'Mammal'), (1, 'Feline')]

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.

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.

(Of course I'm none too sure I could do that after many years of
hacking Java vs a few weeks of Python!)

Take a look at http://ece.arizona.edu/~edatools/Python/Exercises/ and
let me know if Animals_2b.py is what you had in mind. If not, can you
edit it to show me what you mean?

-- Dave

Jul 18 '05 #11

P: n/a
Op 2004-05-10, James Moughan schreef <mo******@tcd.ie>:
David MacQuigg <dm*@gain.com> wrote in message news:<88********************************@4ax.com>. ..
On 8 May 2004 07:07:09 -0700, mo******@tcd.ie (James Moughan) wrote:
>David MacQuigg <dm*@gain.com> wrote in message news:<4a********************************@4ax.com>. ..
>> On 7 May 2004 06:31:51 -0700, mo******@tcd.ie (James Moughan) wrote:

<snip>
>> Also, if you are calling
>> a function that has an instance variable ( .length ) and no instance
>> has been set by a prior binding, you would need to set __self__
>> manually.
>> __self__ = foo; print FooLen()
>
>???!!!???
>
>This is what I was talking about in my first post, global variables
>which change depending on where you are in the code... as I understand
>what you're saying, __self__ will have to be set, then reset when a
>method is called from within a method and the exits. And __self__
>could presumably be changed halfway through a method, too. I'm sorry,
>I don't see this as being more explicit or simpler.


The setting of __self__ happens automatically, just like the setting
of the first argument in a call from an instance. The user doesn't
have to worry about it.


Explicit is better than implicit, especially in getting people to
understand things. :)


I'm a bit sick of this argument. There is a lot om implicity
going on in python. if obj belongs to cls then obj.method()
is syntactic sugar for cls.method(obj). That looks like
a big implicite way to handle things in python.

Likewise all those magical methods to emulate numerical
types or containers or sequences or all ways that allow
the python programmer to do things implicitely throught
the use of operators instead of through explicitely calling
the desired method.

If implicit is such a negative thing to be avoided whenever
possible, python would look a lot different and I probably
wouldn't be using it.

--
Antoon Pardon
Jul 18 '05 #12

P: n/a
Antoon Pardon <ap*****@forel.vub.ac.be> wrote in message news:<sl********************@trout.vub.ac.be>...
Op 2004-05-10, James Moughan schreef <mo******@tcd.ie>:
David MacQuigg <dm*@gain.com> wrote in message news:<88********************************@4ax.com>. ..
On 8 May 2004 07:07:09 -0700, mo******@tcd.ie (James Moughan) wrote:

>David MacQuigg <dm*@gain.com> wrote in message news:<4a********************************@4ax.com>. ..
>> On 7 May 2004 06:31:51 -0700, mo******@tcd.ie (James Moughan) wrote: <snip> >> Also, if you are calling
>> a function that has an instance variable ( .length ) and no instance
>> has been set by a prior binding, you would need to set __self__
>> manually.
>> __self__ = foo; print FooLen()
>
>???!!!???
>
>This is what I was talking about in my first post, global variables
>which change depending on where you are in the code... as I understand
>what you're saying, __self__ will have to be set, then reset when a
>method is called from within a method and the exits. And __self__
>could presumably be changed halfway through a method, too. I'm sorry,
>I don't see this as being more explicit or simpler.

The setting of __self__ happens automatically, just like the setting
of the first argument in a call from an instance. The user doesn't
have to worry about it.
Explicit is better than implicit, especially in getting people to
understand things. :)


I'm a bit sick of this argument. There is a lot om implicity
going on in python. if obj belongs to cls then obj.method()
is syntactic sugar for cls.method(obj).


Well, it is... and it isn't. ``method'' could be bound to cls, cls's
super-class, or to obj itself.
That looks like
a big implicite way to handle things in python.
You could look at it like that. I'd say it's a way of making code
generic, and hiding the workings of a subsystem; lots of good things
in Python wouldn't be possible without this type of abstraction.
Likewise all those magical methods to emulate numerical
types or containers or sequences or all ways that allow
the python programmer to do things implicitely throught
the use of operators instead of through explicitely calling
the desired method.

If implicit is such a negative thing to be avoided whenever
possible, python would look a lot different and I probably
wouldn't be using it.


Sure; you have to have some understood conventions. In effect that's
all any higher-level programming language than assembler is. IMO, in
the case in point, the explicit self makes OO easier to undersstand;
YMMV, of course, as it does with Dave. :)
Jul 18 '05 #13

P: n/a
On 10 May 2004 16:53:06 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<88********************************@4ax.com>. ..
On 8 May 2004 07:07:09 -0700, mo******@tcd.ie (James Moughan) wrote:
< snip topics we have finished >
You are not solving a problem; that's the problem. :) If there were a
real programming task then it would be more trivial to show why your
object model is broken.
I could give you an example from IC Design, but for the course I
teach, I chose to use a similar hierarchy based on something everyone
would understand - a taxonomy of animals. Nothing in this example is
something you wouldn't find in a real program to model an integrated
circuit. Instead of animal names like Cat, we would have the names of
cells in the hierarchy, names like bgref25a. Instead of a variable to
count the number of animals at each level, we might have several
variables to track the total current on each of several supply lines.
Like the counts in the Animals.py hierarchy, we need the total current
to each cell, including all of its subcells.

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.

<snip>
If you can't take it below 70 pages and you only have 4 hours... maybe
it's not such a great idea to try this? I can't see your students
benefiting from what you're proposing to do, if you have so little
time.
I think I could do it in 30 pages and 4 hours total ( lecture, lab,
and homework ), but not if I need to cover the topics that both Mark
Lutz and I consider important to basic OOP in the current version of
Python. The 30 pages assumes the unification of methods and functions
that I have proposed.

<snip>
>> >OK: "The whole idea of having these structures in any program is
>> >wrong."
>> >
>> >Firstly, the program uses a class hierarchy as a data structure. That
>> >isn't what class heirarchies are designed for, and not how they should
>> >be used IMO. But it's what any bright student will pick up from the
>> >example.
>>
>> The classes contain both data and functions. The data is specific to
>> each class. I even show an example of where the two-class first
>> example forced us to put some data at an inappropriate level, but with
>> a four class hierarchy, we can put each data item right where it
>> belongs.
>>
>
>The data is not specific to the class. It's specific to the class and
>it's subclasses. Subclasses should be dependent on the superclass,
>and generally not the other way around.


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.

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.
>> Nothing in the Bovine class can affect anything in a Cat. Feline and
>> Bovine are independent branches below Mammal. Adding a Mouse class
>> anywhere other than in the chain Cat - Feline - Mammal - Animal cannot
>> affect Cat. Could you give a specific example?
>>
>
>Say someone adds a mouse class but doesn't call the constructor for
>Mammal. The data produced by mammal and therefore cat is now
>incorrect, as instances of mouse are not included in your count. In a
>real example, anything might be hanging on that variable - so e.g.
>someone adds some mouse instances and the program crashes with an
>array index out of bounds (or whatever the Pythonic equivalent is :) )
>, or maybe we just get bad user output. This type of behaviour is
>damn-near impossible to debug in a complex program, because you didn't
>change anything which could have caused it. It's caused by what you
>didn't do.


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.)
While errors are always going to happen, OOP calls on some conventions
to minimize them. The most absolutely vital of these is that it's
clear what can break what. Generally I should never be able to break
a subsystem by breaking it's wrapper; definitely I should never be
able to break a superclass by breaking it's subclass; and I
*certainly* shouldn't be able to break a part of the system by
changing something unconnected to it. The whole of OOP derives, more
or less directly, from these principles. Expressions like 'A is a
part/type of B' derive from this philosophy, not the other way around.
Sounds good.
Your program breaks with this concept. It allows an event in Cat to
affect data in Mammal and in Animal, which also has knock-on effects
for every other subclass of these. Therefore it is bad object
oriented programming.
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 2 microamps to
the core currents in a bandgap voltage reference, you add that same 2
microamps to the total supply current.

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.
It takes us back to the days before even structured programming, when
no-one ever had any idea what the effects of altering or adding a
piece of code would be.

It is therefore not a good teaching example. :)
I'll need to see something better before I abandon the curent example.
The problem may be our expectations of OOP. I see classes as modeling
the real world, including variables that are altered by changes in
subclasses. You seem to have some computer science notion of what a
class should be. I'm not saying its wrong, but unless it helps me
solve my real-world problems, in a better way than what I am doing
now, I won't use it.

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'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.
>> Chaining them together results in a
>> sequence of calls, and a sequence of outputs that is exactly what we
>> want. The nice thing about separating the total "show" functionality
>> into parts specific to each class is that when we add a class in the
>> middle, as I did with Feline, inserted between Mammal and Cat, it is
>> real easy to change the Cat class to accomodate the insertion.
>>
>> Python has a 'super' function to facilitate this kind of chaining.
>> Michele Simionato's 'prototype.py' module makes 'super' even easier to
>> use. Instead of having Cat.show() call Mammal.show() I can now just
>> say super.show() and it will automatically call the show() function
>> from whatever class is the current parent. Then when I add a Feline
>> class between Mammal and Cat, I don't even need to change the
>> internals of Cat.
>
>That's fine - providing you're not using a class heirarchy to store
>data. It's not the act of calling a method in a super-class which is
>a bad idea, it's the way you are making *the numbers outputted* from
>cat dependent of actions taken *or not taken* in another class
>*completely outside cat's scope*.


Seems like this is the way it has to be if you want to increment the
counts for Cat and all its ancestors whenever you create a new
instance of Cat. Again, I'm not understanding the problem you are
seeing. You seem to be saying there should be only methods, not data,
stored in each class.


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.

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.
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. 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.
>> In one syntax we need special "static methods" to handle calls where a
>> specific instance is not available, or not appropriate. In another
>> syntax we can do the same thing with one universal function form.


To try and get to the bottom of this, I re-wrote the Animals.py
example, following what I think are your recommendations on moving the
static methods to module-level functions. I did not move the data out
of the classes, because that makes no sense to me at all.


*Sigh* No, I must say that doesn't help much. :-\

As I said, there is something wrong with the whole idea behind it; the
design needs refactoring, not individual lines of code.

Having said that, I'll try to redact the issues as best I can, on the
basis that it may illustrate what I mean.

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.

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.
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.
???
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.
class Collective:
class base: pass

def startup(self, coll, root):
#wrapper class to count creations of classes
self.root = root
class wrapper:
def __init__(self, name, c):
self.mycount = 0
self.c = c
self.name = name
def __call__(self, *arg):
tmp = self.c(*arg)
self.mycount += 1
return self.c(*arg)
self.wrapper = wrapper
#replace every class derived from root with a wrapper
#plus build a table of the
self.wrap_list = []
for name, elem in coll.__dict__.items():
try:
if issubclass(elem, self.root):
tmp = wrapper(name, elem)
self.__dict__[name] = tmp
self.wrap_list.append(tmp)
except: pass

#when subclassing, override this
#call startup with the class name
#and the root of the class heirarchy
def __init__(self):
self.startup(Collective, self.base)

#here's the stuff to do the counting
#this could be much faster with marginally more work
#exercise for the reader... ;)

def get_counts(self, klass):
counts = [ (x.c, (self.get_sub_count(x), x.name)) \
for x in self.super_classes(klass) ]
counts.append( (klass.c, (self.get_sub_count(klass),
klass.name)) )
counts.sort(lambda x, y: issubclass(x[0], y[0]))
return [x[-1] for x in counts]

def get_sub_count(self, klass):
count = klass.mycount
for sub in self.sub_classes(klass):
count += sub.mycount
return count
def super_classes(self, klass):
return [x for x in self.wrap_list if issubclass(klass.c, x.c)
\
and not x.c is klass.c]
def sub_classes(self, klass):
return [x for x in self.wrap_list if issubclass(x.c, klass.c)
\
and not x.c is klass.c]

So we can now do:

class animal_farm(Collective):
class Animal: pass
class Mammal(Animal): pass
class Bovine(Mammal): pass
class Feline(Mammal): pass
class Cat(Feline): pass
def __init__(self):
self.startup(animal_farm, self.Animal)
a_farm = animal_farm()
cat = a_farm.Cat()
feline = a_farm.Mammal()
print a_farm.get_counts(a_farm.Feline)
[(2, 'Animal'), (2, 'Mammal'), (1, 'Feline')]


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.

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

-- Dave
(Of course I'm none too sure I could do that after many years of
hacking Java vs a few weeks of Python!)

Take a look at http://ece.arizona.edu/~edatools/Python/Exercises/ and
let me know if Animals_2b.py is what you had in mind. If not, can you
edit it to show me what you mean?

-- Dave


Jul 18 '05 #14

P: n/a
On 11 May 2004 18:54:56 -0700, mo******@tcd.ie (James Moughan) wrote:
Sure; you have to have some understood conventions. In effect that's
all any higher-level programming language than assembler is. IMO, in
the case in point, the explicit self makes OO easier to undersstand;
YMMV, of course, as it does with Dave. :)


I really hate to get involved in this "explicit self" debate, as I
know it has been debated ad-nauseum for many years, but I wish people
would not attribute to me a point-of-view that I do not have. For me
the "explicit self" is *not* the issue. I have a slight preference
for .var over self.var or $var, but this is a matter of personal
preference, not an implicit vs explicit question.

My definition of explicit is that you can tell the meaning of a
statement without reference to the surrounding code. By that
definition all of the above forms are explicit. They are all instance
variables, and there is no other interpretation. The choice between
them is a *minor issue*, and I trust GvR to make these choices.

The real issue is whether we can unify all forms of functions and
methods. This requires we do something different with 'self'. From a
unification standpoint, an equally acceptable solution is that we add
'self' to *all* functions and methods, whether they need it or not.

The problem with rules like "explicit is better than implicit" is that
some people take them too literally. Sometimes it seems like quoting
a rule is a substitute for thinking. We need to balance all these
rules, and add a good measure of common sense to reach the true goal.

My goal is overall simplicity for the syntax needed to solve the kind
of problems that a non-CIS technical professional might encounter.
That includes static methods, but not metaclasses. That could change
if someone convinced me that static methods are unnecessary or that
metaclasses are essential to solve a real-world problem.

Meanwhile I'm happy to just watch the wizards play with metaclasses,
and I don't care if their brains explode. :>) I'm also happy to use
what they develop with metaclasses. Case in point - Michele
Simionato's prototype module. This is easy to use, even if I don't
understand how it works. To me, its like a C module. I don't feel I
need to understand the internals.

I feel entirely differently about excess complexity in syntax my
students and clients are likely to write or need to understand.

-- Dave

Jul 18 '05 #15

P: n/a
David MacQuigg wrote:
I could give you an example from IC Design, but for the course I
teach, I chose to use a similar hierarchy based on something everyone
would understand - a taxonomy of animals. Nothing in this example is
something you wouldn't find in a real program to model an integrated
circuit.


Perhaps it would be better to give these people examples
based on actual parts of an integrated circuit, then?
If they can see an immediate, realistic use for what
you're teaching, they're likely to grasp and remember
it more easily. Cats and mammals have the potential to
be just as boring and artificial-seeming as parts and
suppliers.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #16

P: n/a
David MacQuigg wrote:
The real issue is whether we can unify all forms of functions and
methods. This requires we do something different with 'self'. From a
unification standpoint, an equally acceptable solution is that we add
'self' to *all* functions and methods, whether they need it or not.]


I still don't see the benefit of unifying them to this degree.
Even if you added 'self' to every function, there would *still*
be two kinds of function, those that use their 'self' and those
that don't. The difference in usage is still there, even if it's
not directly reflected in the syntax. I can't see the point in
trying to pretend otherwise.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #17

P: n/a
David MacQuigg <dm*@gain.com> wrote in message news:<q3********************************@4ax.com>. ..
On 10 May 2004 16:53:06 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<88********************************@4ax.com>. ..
On 8 May 2004 07:07:09 -0700, mo******@tcd.ie (James Moughan) wrote:
< snip topics we have finished >
You are not solving a problem; that's the problem. :) If there were a
real programming task then it would be more trivial to show why your
object model is broken.
I could give you an example from IC Design, but for the course I
teach, I chose to use a similar hierarchy based on something everyone
would understand - a taxonomy of animals. Nothing in this example is
something you wouldn't find in a real program to model an integrated
circuit. Instead of animal names like Cat, we would have the names of
cells in the hierarchy, names like bgref25a. Instead of a variable to
count the number of animals at each level, we might have several
variables to track the total current on each of several supply lines.
Like the counts in the Animals.py hierarchy, we need the total current
to each cell, including all of its subcells.


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

But even this isn't how you would organize an accountancy program.
You'd use a database.

<snip>
If you can't take it below 70 pages and you only have 4 hours... maybe
it's not such a great idea to try this? I can't see your students
benefiting from what you're proposing to do, if you have so little
time.
I think I could do it in 30 pages and 4 hours total ( lecture, lab,
and homework ), but not if I need to cover the topics that both Mark
Lutz and I consider important to basic OOP in the current version of
Python. The 30 pages assumes the unification of methods and functions
that I have proposed.


70 -> 30 because of this? Really?

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.
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.
While errors are always going to happen, OOP calls on some conventions
to minimize them. The most absolutely vital of these is that it's
clear what can break what. Generally I should never be able to break
a subsystem by breaking it's wrapper; definitely I should never be
able to break a superclass by breaking it's subclass; and I
*certainly* shouldn't be able to break a part of the system by
changing something unconnected to it. The whole of OOP derives, more
or less directly, from these principles. Expressions like 'A is a
part/type of B' derive from this philosophy, not the other way around.
Sounds good.
Your program breaks with this concept. It allows an event in Cat to
affect data in Mammal and in Animal, which also has knock-on effects
for every other subclass of these. Therefore it is bad object
oriented programming.


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 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.
It takes us back to the days before even structured programming, when
no-one ever had any idea what the effects of altering or adding a
piece of code would be.

It is therefore not a good teaching example. :)


I'll need to see something better before I abandon the curent example.
The problem may be our expectations of OOP. I see classes as modeling
the real world, including variables that are altered by changes in
subclasses. You seem to have some computer science notion of what a
class should be. I'm not saying its wrong, but unless it helps me
solve my real-world problems, in a better way than what I am doing
now, I won't use it.


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

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

In the case of a real zoo, I can pretty much guarantee that it would
begin
import PySQLdb
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 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. :)

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

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.
-- Dave
(Of course I'm none too sure I could do that after many years of
hacking Java vs a few weeks of Python!)

Take a look at http://ece.arizona.edu/~edatools/Python/Exercises/ and
let me know if Animals_2b.py is what you had in mind. If not, can you
edit it to show me what you mean?

-- Dave

Jul 18 '05 #18

P: n/a
Op 2004-05-12, David MacQuigg schreef <dm*@gain.com>:
On 11 May 2004 18:54:56 -0700, mo******@tcd.ie (James Moughan) wrote:
Sure; you have to have some understood conventions. In effect that's
all any higher-level programming language than assembler is. IMO, in
the case in point, the explicit self makes OO easier to undersstand;
YMMV, of course, as it does with Dave. :)


I really hate to get involved in this "explicit self" debate, as I
know it has been debated ad-nauseum for many years, but I wish people
would not attribute to me a point-of-view that I do not have. For me
the "explicit self" is *not* the issue. I have a slight preference
for .var over self.var or $var, but this is a matter of personal
preference, not an implicit vs explicit question.

My definition of explicit is that you can tell the meaning of a
statement without reference to the surrounding code. By that
definition all of the above forms are explicit. They are all instance
variables, and there is no other interpretation. The choice between
them is a *minor issue*, and I trust GvR to make these choices.

The real issue is whether we can unify all forms of functions and
methods. This requires we do something different with 'self'. From a
unification standpoint, an equally acceptable solution is that we add
'self' to *all* functions and methods, whether they need it or not.


One could argue that all forms of methods and functions are unified.
It is just that python does some magic so that the method you
access from an object is not the actual function but the curried
function with the object.

If we have the following class:
class Incrementor:

def __init__(self, first_value):
self.value = first_value

def next(self):
result = self.value
self.value += 1
return result
We could rewrite it as follows without methods -- except for __init__ --
and get the same object functionality :
class Incrementor:

def __init__(self, first_value):

def next(self):

result = self.value
self.value += 1
return result

self.value = first_value
self.next = curry(next)(self) # or self.next = curry(next,self)
# depending on the curry version
Now if explaining currying functions to your students will help
them understand, I don't know, although I don't find it that
complicated.

--
Antoon Pardon
Jul 18 '05 #19

P: n/a

"Antoon Pardon" <ap*****@forel.vub.ac.be> wrote in message
news:sl********************@trout.vub.ac.be...
One could argue that all forms of methods and functions are unified.
It is just that python does some magic so that the method you
access from an object is not the actual function but the curried
function with the object.


My view is similar: in Python, a method is a dressed up (wrapped) function.
'Unification of methods and functions' reads to me like 'unification of
dressed-up bodies and naked bodies'. What would that mean? One thing I
like about Python is that there is only function-body syntax and not a
separate, slightly different method-body syntax. To me, having only one
type of code body *is* unification. So it is hard for me to see what is
being proposed. Introducing a syntax like '.var' that would only be
meaningful in a method and not a function would be dis-unification.

Terry J. Reedy


Jul 18 '05 #20

P: n/a
I'm beginning to understand what you are saying. Thanks for hanging
in there.

On 12 May 2004 20:51:20 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<q3********************************@4ax.com>. ..
On 10 May 2004 16:53:06 -0700, mo******@tcd.ie (James Moughan) wrote:
>David MacQuigg <dm*@gain.com> wrote in message news:<88********************************@4ax.com>. ..
>> On 8 May 2004 07:07:09 -0700, mo******@tcd.ie (James Moughan) wrote:
< snip topics we have finished >
>You are not solving a problem; that's the problem. :) If there were a
>real programming task then it would be more trivial to show why your
>object model is broken.


I could give you an example from IC Design, but for the course I
teach, I chose to use a similar hierarchy based on something everyone
would understand - a taxonomy of animals. Nothing in this example is
something you wouldn't find in a real program to model an integrated
circuit. Instead of animal names like Cat, we would have the names of
cells in the hierarchy, names like bgref25a. Instead of a variable to
count the number of animals at each level, we might have several
variables to track the total current on each of several supply lines.
Like the counts in the Animals.py hierarchy, we need the total current
to each cell, including all of its subcells.


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

<snip>
>If you can't take it below 70 pages and you only have 4 hours... maybe
>it's not such a great idea to try this? I can't see your students
>benefiting from what you're proposing to do, if you have so little
>time.


I think I could do it in 30 pages and 4 hours total ( lecture, lab,
and homework ), but not if I need to cover the topics that both Mark
Lutz and I consider important to basic OOP in the current version of
Python. The 30 pages assumes the unification of methods and functions
that I have proposed.


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

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.
>While errors are always going to happen, OOP calls on some conventions
>to minimize them. The most absolutely vital of these is that it's
>clear what can break what. Generally I should never be able to break
>a subsystem by breaking it's wrapper; definitely I should never be
>able to break a superclass by breaking it's subclass; and I
>*certainly* shouldn't be able to break a part of the system by
>changing something unconnected to it. The whole of OOP derives, more
>or less directly, from these principles. Expressions like 'A is a
>part/type of B' derive from this philosophy, not the other way around.


Sounds good.
>Your program breaks with this concept. It allows an event in Cat to
>affect data in Mammal and in Animal, which also has knock-on effects
>for every other subclass of these. Therefore it is bad object
>oriented programming.


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.
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.
>It takes us back to the days before even structured programming, when
>no-one ever had any idea what the effects of altering or adding a
>piece of code would be.
>
>It is therefore not a good teaching example. :)


I'll need to see something better before I abandon the curent example.
The problem may be our expectations of OOP. I see classes as modeling
the real world, including variables that are altered by changes in
subclasses. You seem to have some computer science notion of what a
class should be. I'm not saying its wrong, but unless it helps me
solve my real-world problems, in a better way than what I am doing
now, I won't use it.


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

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

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

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

-- Dave

Jul 18 '05 #21

P: n/a
On Thu, 13 May 2004 10:56:58 -0400, "Terry Reedy" <tj*****@udel.edu>
wrote:
"Antoon Pardon" <ap*****@forel.vub.ac.be> wrote in message
news:sl********************@trout.vub.ac.be...
One could argue that all forms of methods and functions are unified.
It is just that python does some magic so that the method you
access from an object is not the actual function but the curried
function with the object.
My view is similar: in Python, a method is a dressed up (wrapped) function.
'Unification of methods and functions' reads to me like 'unification of
dressed-up bodies and naked bodies'. What would that mean?


By unification, I mean making the calling sequence identical for all
methods and functions. No more special syntax for lambda functions,
static methods, etc. No time wasted on these topics in a text like
Learning Python. A simpler presentation of OOP, based on what
students already know at this point - functions, modules, and global
variables.
One thing I
like about Python is that there is only function-body syntax and not a
separate, slightly different method-body syntax. To me, having only one
type of code body *is* unification. So it is hard for me to see what is
being proposed.
The one fundamental difference between methods and functions is the
presence of instance variables in methods. You can hide that
difference with the self.var trick. You can hide it even further by
not using 'self', but some other word that looks just like the other
variable names in the function. None of this removes the actual
difference, or makes learning about instance variables any easier for
the student.

The choice of how to identify instance variables is a minor issue. I
think it is actually better not to *hide* the instance variables, but
to *highlight* them. Ruby uses $var for instance variables. Prothon
uses a leading dot, which is quite visible in most code editors.

The real issue is not self.var vs .var It is the complexity resulting
from having different calling sequences. Compare the 96-page
presentation of OOP in Learning Python, 2nd ed. to what I have written
at http://ece.arizona.edu/~edatools/Python/Prototypes.doc
In 7 pages, I can present the basics of OOP. Adding examples and
exercises will probably bring this to 30 pages. The key to this
simplification is not having to deal with unnecessary complexities,
like lambda functions, static methods, etc.
Introducing a syntax like '.var' that would only be
meaningful in a method and not a function would be dis-unification.


Actually, .var could be used in a function outside a class, just as
you can now use self.var in the current function syntax. Before using
a function with .var, the global variable __self__ must be set. This
is normally done by calling the function from an instance. Before
using a function with self.var, the first argument must be set. This
is normally done by calling the function fom an instance.

To say that adding an instance variable to a function breaks unity is
like saying that adding a global variable to a function breaks unity.
Adding an instance variable to a function simply makes the function
dependent on an external variable. Same with a global.

-- Dave

Jul 18 '05 #22

P: n/a
David MacQuigg wrote:
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.


It's still not clear whether the hierarchy you're talking
about is a hierarchy of *kinds* of cells (i.e. a class
hierarchy), or a physical hierarchy of actual cells (i.e.
an instance hierarchy).

Both of these hierarchies can exist at the same time, and
they are different.

For example, you might have a kind of cell called NandGate,
and a kind of cell called Transistor. An instance of NandGate
contains instances of Transistor -- this is an instance
hierarchy. But Transistor is not a subclass of NandGate --
that would imply a Transistor is a kind of NandGate, which
is not true.

On the other hand, NandGate may well be a subclass of Gate,
and Gate and Transistor may be subclasses of Component. But
that doesn't mean Gates *contain* NandGates.

If prototype-based programming leads to confusion between
class hierarchies and instance hierarchies, the I would
say it's not helpful.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #23

P: n/a
David MacQuigg <dm*@gain.com> wrote in message news:<ut********************************@4ax.com>. ..

## This does not:
uf = Cat.talk # Save an unbound function.
uf() #=>
# AttributeError: type object 'Cat' has no attribute 'name'

I would like to be able to do something like:
__self__ = cat2
uf()


So, if I understand correcly, you want Cat.talk to be the same than
cat.talk and Cat to be a prototype just as cat (so Cat must
increase the animal counter and its __init__ method has to
be called at definition time). It is enough to change a
couple of lines in the prototype module:

import sys
from types import FunctionType
from inspect import isfunction

class methodwrapper(object):
def __init__(self,func,cls):
self.__func__ = func
self.__cls__ = cls
def __get__(self,none,cls):
globs=sys.modules[cls.__module__].__dict__.copy()
globs["self"] = globs[cls.__name__] = cls
globs["super"] = super(self.__cls__,cls)
return FunctionType(
self.__func__.func_code,
globs,
self.__func__.func_name,
self.__func__.func_defaults,
self.__func__.func_closure)

class _Prototype(type):
def __init__(cls,name,bases,dic):
for k,v in dic.iteritems():
if isfunction(v):
setattr(cls,k,methodwrapper(v,cls))
super(_Prototype,cls).__init__(name,bases,dic)
cls.__init__()
def __call__(cls,*args,**kw):
newcls = type("Prototype:%s" % cls.__name__,(cls,),
{"__module__": cls.__module__})
newcls.__init__(*args,**kw)
return newcls

class Prototype(object):
__metaclass__=_Prototype
def __init__(*args,**kw):
pass

### END

Here is an example of usage:

from prototype import Prototype

class Animal(Prototype):
numAnimals = 0
home = "Earth"
def __init__():
Animal.numAnimals += 1
def show():
print "Animals:", self.numAnimals

class Feline(Animal):
genus="Feline"
def __init__(name="unknown",sound=""):
super.__init__()
self.name=name
self.sound=sound
def talk():
print "%s talking: %s!" % (self.genus,self.sound)

class Cat(Feline): # created here
numCats = 0
def __init__ ( n = "unknown", s = "Meow" ):
super.__init__(n,s)
Cat.numCats += 1
def show():
super.show()
print " Cats:", self.numCats
def talk():
print "My name is", self.name
print "I am a %s from %s" % (self.genus, self.home)
super.talk()

Cat.talk()

cat = Cat("Garfield")
cat.talk()
Jul 18 '05 #24

P: n/a
Op 2004-05-13, David MacQuigg schreef <dm*@gain.com>:
On Thu, 13 May 2004 10:56:58 -0400, "Terry Reedy" <tj*****@udel.edu>
wrote:
"Antoon Pardon" <ap*****@forel.vub.ac.be> wrote in message
news:sl********************@trout.vub.ac.be...
One could argue that all forms of methods and functions are unified.
It is just that python does some magic so that the method you
access from an object is not the actual function but the curried
function with the object.
My view is similar: in Python, a method is a dressed up (wrapped) function.
'Unification of methods and functions' reads to me like 'unification of
dressed-up bodies and naked bodies'. What would that mean?


By unification, I mean making the calling sequence identical for all
methods and functions.


It already is. It is just that the function you have defined within
the class, is not the function you call through the object.
No more special syntax for lambda functions,
All right I understand that.
static methods, etc. No time wasted on these topics in a text like
Learning Python. A simpler presentation of OOP, based on what
students already know at this point - functions, modules, and global
variables.
A method is just a function. The special calling form for methods
is not object specific. With a little extra work you can have exactly
the same calling form for classes with no methods.
One thing I
like about Python is that there is only function-body syntax and not a
separate, slightly different method-body syntax. To me, having only one
type of code body *is* unification. So it is hard for me to see what is
being proposed.


The one fundamental difference between methods and functions is the
presence of instance variables in methods.


No it is not. The instance variables you speak of within a method
are just the instance variables of a parameter. It can work just
as well with a function that is written outside the class. you
just need to wrap it somewhat if you want the same calling sequence
but that is all and this wrapping can happen outside the class too.

You can hide that
difference with the self.var trick. You can hide it even further by
not using 'self', but some other word that looks just like the other
variable names in the function. None of this removes the actual
difference, or makes learning about instance variables any easier for
the student.
Maybe you should start with classes without methods then. Just

class Whatever:
pass.

Now you can make objects of this class, give them instance variables
and write a function that expects a whatever object as first argument
en let it play with the instance variables of the argument. If you wish
you can call that first argument self it doesn't really matter. There
is nothing method or function specific here.
The choice of how to identify instance variables is a minor issue. I
think it is actually better not to *hide* the instance variables, but
to *highlight* them. Ruby uses $var for instance variables. Prothon
uses a leading dot, which is quite visible in most code editors.

The real issue is not self.var vs .var It is the complexity resulting
from having different calling sequences.
There really aren't different calling sequences, just to different
related function. Lets consider the two following functions.

def addf(a, b):

return a + b
def addc(a):

def addh(b):

return a + b

return addh
Now you can use both to add two numbers, but the calling sequence
will be different: You call addf(n1, n2), but you call addc(n1)(n2).
The seconde is called the curried version of the first.
Now if I know I will need to add 2 to al lot of numbers I can
do the following: add2 = addc(2) and now when I need to add 2 to
a number I can just call add2(n). So now I have a function that
has the value 2 bound to it. In a way it has become a method of
2. We can do that even explicitly

class Int(int):
pass

two = Int(2)
add2 = addc(two)
two.add = add2
two, add2(5), two.add(6), addf(two, 7)


(2, 7, 8, 9)
The only thing that is happening is that if you write a function
within a class, python transforms it into the curried version
and does the rest of the things implicitely that I have done
above explicitly.

Compare the 96-page
presentation of OOP in Learning Python, 2nd ed. to what I have written
at http://ece.arizona.edu/~edatools/Python/Prototypes.doc
In 7 pages, I can present the basics of OOP. Adding examples and
exercises will probably bring this to 30 pages. The key to this
simplification is not having to deal with unnecessary complexities,
like lambda functions, static methods, etc.
Introducing a syntax like '.var' that would only be
meaningful in a method and not a function would be dis-unification.
Actually, .var could be used in a function outside a class, just as
you can now use self.var in the current function syntax. Before using
a function with .var, the global variable __self__ must be set. This
is normally done by calling the function from an instance. Before
using a function with self.var, the first argument must be set. This
is normally done by calling the function fom an instance.

To say that adding an instance variable to a function breaks unity is
like saying that adding a global variable to a function breaks unity.
It does in some sense.

I also could have written the add2 function as follows

self = 2
def add2(n):

return self + n
I don't think that would have been a good idea. Having __self__ being
a global variable is disaster waiting to happen. What if a method
calls another method, what if you use threads?
Adding an instance variable to a function simply makes the function
dependent on an external variable. Same with a global.


But currently in python "self" is not an external variable, it is
just the first argument of the function. Manipulating an argument
is different from manipulating an external variable.

--
Antoon Pardon
Jul 18 '05 #25

P: n/a
On Fri, 14 May 2004 14:25:15 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
David MacQuigg wrote:
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.
It's still not clear whether the hierarchy you're talking
about is a hierarchy of *kinds* of cells (i.e. a class
hierarchy), or a physical hierarchy of actual cells (i.e.
an instance hierarchy).


In the Animals_2 example we are discussing
http://ece.arizona.edu/~edatools/Python/Animals.py, the classes (
Animal, Mammal, Feline, Cat ) are *kinds* of animals. The instances (
cat1, cat2, etc. ) represent the actual objects in the inventory.

James has been telling me it is terribly wrong to keep data as
attributes of classes, or to make the methods in each class dependent
on superclasses. The latest suggestion (if I understand it correctly)
is to make a dummy instance from each class, just so we can store data
that is common to the whole class. The "functional spec" above
deliberately avoids any requirement for classes, instances, other
implementation detail.

I'm still trying to come up with a simple example using James' latest
suggestions (something simpler than the automatic class generator he
wrote).

class Feline(Mammal):
_numFelines = 0
genus = "feline"
def __init__(self):
Mammal.__init__(self)
Feline._numFelines += 1
def show(self):
Mammal.show(self)
print " Felines:", Feline._numFelines
## show = staticmethod(show)
feline = Feline()

By adding an extra line after each class, we can eliminate the static
methods. Now when I want to show the characteristics of the Feline
class, I can call feline.show() and get the same result as I had
before using the static method Feline.show().

I still need to move the data to the instance, and get rid of the
super calls ( Mammal.__init__ and Mammal.__show__ ) I am having great
difficulty understanding James' suggestions. Maybe you could help
clarify what is wrong with the original example, and what we need to
do to fix it.
Both of these hierarchies can exist at the same time, and
they are different.
This is true, and the real program will be more complex than the
Animals_2 example. In Animals_2 it is sufficient to know the total
number of Cats. In the real program, we will need the total number of
gates of type NAND2. In *addition*, we will need to know the total
current to each instance of NAND2, including all the subcircuits under
that instance. Those totals, in my way of thinking, should be stored
on each NAND2 instance.
For example, you might have a kind of cell called NandGate,
and a kind of cell called Transistor. An instance of NandGate
contains instances of Transistor -- this is an instance
hierarchy. But Transistor is not a subclass of NandGate --
that would imply a Transistor is a kind of NandGate, which
is not true.

On the other hand, NandGate may well be a subclass of Gate,
and Gate and Transistor may be subclasses of Component. But
that doesn't mean Gates *contain* NandGates.

If prototype-based programming leads to confusion between
class hierarchies and instance hierarchies, the I would
say it's not helpful.


Let's make sure we are talking about the same thing. If by
prototype-based programming, you mean Prothon-style, classless
prototypes, then I agree. The benefits of having a distinction
between classes and instances, in my current understanding of
"classless programming", far outweigh any simplification that might
flow from having just one "tier" in the object model. That is why I
have avoided in my proposal the "classless" aspects of Prothon.

On the other hand, if you are suggesting that something in my current
proposal will lead to confusion, I would like more specifics.

-- Dave

Jul 18 '05 #26

P: n/a
On 14 May 2004 12:29:32 GMT, Antoon Pardon <ap*****@forel.vub.ac.be>
wrote:
Op 2004-05-13, David MacQuigg schreef <dm*@gain.com>:
On Thu, 13 May 2004 10:56:58 -0400, "Terry Reedy" <tj*****@udel.edu>
wrote:
"Antoon Pardon" <ap*****@forel.vub.ac.be> wrote in message
news:sl********************@trout.vub.ac.be.. .
One could argue that all forms of methods and functions are unified.
It is just that python does some magic so that the method you
access from an object is not the actual function but the curried
function with the object.

My view is similar: in Python, a method is a dressed up (wrapped) function.
'Unification of methods and functions' reads to me like 'unification of
dressed-up bodies and naked bodies'. What would that mean?
By unification, I mean making the calling sequence identical for all
methods and functions.


It already is. It is just that the function you have defined within
the class, is not the function you call through the object.


In the current sytnax, we need a special "staticmethod" statement to
define a method we can call without an instance. In the proposed
syntax, there is no distinction between the different method styles,
and no need for special syntax. A "staticmethod" has the same
arguments as normal method, and the same as an ordinary function.
This is what I mean by having the same "calling sequence".

For an example of how unification simplifies the presentation of OOP,
see the first 8 pages of
http://ece.arizona.edu/~edatools/Python/Prototypes.doc
The choice of how to identify instance variables is a minor issue. I
think it is actually better not to *hide* the instance variables, but
to *highlight* them. Ruby uses $var for instance variables. Prothon
uses a leading dot, which is quite visible in most code editors.

The real issue is not self.var vs .var It is the complexity resulting
from having different calling sequences.


There really aren't different calling sequences, just to different
related function.


In the Animals.py example at http://ece.arizona.edu/~edatools/Python/
there is a method Mammal.show(). Normally, the calling sequence is
Mammal.show(cat1)
where cat1 is just an instance needed to satisfy the requirement for a
special first argument. The method does not *use* cat1, and in
general, there may not be an instance of every class available. If we
convert Mammal.show to a static method, we don't need the special
first argument. The calling sequence is then
Mammal.show()

Do you understand what I mean by "different calling sequences"?

<snip>
Having __self__ being
a global variable is disaster waiting to happen. What if a method
calls another method, what if you use threads?


If a method calls another method, the same thing happens in the
proposed syntax as in current Python. Normally, the current instance
doesn't change.
Python 2>>> Mammal.talk(self)
Python 3>>> Mammal.talk()
If you want to *over-ride* the normal behavior and *force* a specific
binding:
Python 2>>> Mammal.talk(cat2)
Python 3>>> __self__ = cat2; Mammal.talk()
I don't know why anyone would ever want to do this within a method,
but it is possible.

I don't understand the problem with threads. Perhaps an example would
help.

-- Dave

Jul 18 '05 #27

P: n/a
Op 2004-05-14, David MacQuigg schreef <dm*@gain.com>:
On 14 May 2004 12:29:32 GMT, Antoon Pardon <ap*****@forel.vub.ac.be>
wrote:
Op 2004-05-13, David MacQuigg schreef <dm*@gain.com>:
On Thu, 13 May 2004 10:56:58 -0400, "Terry Reedy" <tj*****@udel.edu>
wrote:
"Antoon Pardon" <ap*****@forel.vub.ac.be> wrote in message
news:sl********************@trout.vub.ac.be. ..
> One could argue that all forms of methods and functions are unified.
> It is just that python does some magic so that the method you
> access from an object is not the actual function but the curried
> function with the object.

My view is similar: in Python, a method is a dressed up (wrapped) function.
'Unification of methods and functions' reads to me like 'unification of
dressed-up bodies and naked bodies'. What would that mean?

By unification, I mean making the calling sequence identical for all
methods and functions.
It already is. It is just that the function you have defined within
the class, is not the function you call through the object.


In the current sytnax, we need a special "staticmethod" statement to
define a method we can call without an instance.


Not realy, this would work just as well:

def __func(a,b):
...
class C:

def __init__(self):

self.func == __func
Why one wouldn't just write func and use it, instead of having
a staticmethod is beyond me, but it you want it be my guest.
In the proposed
syntax, there is no distinction between the different method styles,
and no need for special syntax. A "staticmethod" has the same
arguments as normal method, and the same as an ordinary function.
This is what I mean by having the same "calling sequence".

For an example of how unification simplifies the presentation of OOP,
see the first 8 pages of
http://ece.arizona.edu/~edatools/Python/Prototypes.doc
The choice of how to identify instance variables is a minor issue. I
think it is actually better not to *hide* the instance variables, but
to *highlight* them. Ruby uses $var for instance variables. Prothon
uses a leading dot, which is quite visible in most code editors.

The real issue is not self.var vs .var It is the complexity resulting
from having different calling sequences.
There really aren't different calling sequences, just to different
related function.


In the Animals.py example at http://ece.arizona.edu/~edatools/Python/
there is a method Mammal.show(). Normally, the calling sequence is
Mammal.show(cat1)
where cat1 is just an instance needed to satisfy the requirement for a
special first argument. The method does not *use* cat1, and in
general, there may not be an instance of every class available. If we
convert Mammal.show to a static method, we don't need the special
first argument. The calling sequence is then
Mammal.show()

Do you understand what I mean by "different calling sequences"?


And do you understand it when I say, that it is not different
calling sequences but different functions. Two functions that
use the same name, are related in functionaly but are different.

Did you understand what happened with the "two" object?

<snip>
Having __self__ being
a global variable is disaster waiting to happen. What if a method
calls another method, what if you use threads?
If a method calls another method, the same thing happens in the
proposed syntax as in current Python. Normally, the current instance
doesn't change.


In current python no global variables are set by a call to a method.
Python 2>>> Mammal.talk(self)
Python 3>>> Mammal.talk()
If you want to *over-ride* the normal behavior and *force* a specific
binding:
Python 2>>> Mammal.talk(cat2)
Python 3>>> __self__ = cat2; Mammal.talk()
I don't know why anyone would ever want to do this within a method,
but it is possible.

I don't understand the problem with threads. Perhaps an example would
help.


You talk about a global variable that is being set, What if two threads
quasi simultaneously call a method. To what value is this global
__self__ set.

--
Antoon Pardon
Jul 18 '05 #28

P: n/a
On Thu, 2004-05-13 at 17:59, David MacQuigg wrote:
The one fundamental difference between methods and functions is the
presence of instance variables in methods.

To me, that statement is missing the whole point. OOP is not procedural
programming. That is the fundamental difference.

"Unifying" the syntax in an attempt to make it easier for your students
to learn probably won't accomplish what you want - one, I don't think
what you propose is easier or better, and two, and more importantly,
simply knowing python's (or any other language's) OOP syntax doesn't
automatically make you an OO guru.

Knowing when, where and why to implement classes is much harder than
learning python's current syntax for building them. Your proposal
doesn't change that.
You can hide that
difference with the self.var trick. You can hide it even further by
not using 'self', but some other word that looks just like the other
variable names in the function. None of this removes the actual
difference, or makes learning about instance variables any easier for
the student.The choice of how to identify instance variables is a minor issue. I
think it is actually better not to *hide* the instance variables, but
to *highlight* them. Ruby uses $var for instance variables. Prothon
uses a leading dot, which is quite visible in most code editors. Actually, .var could be used in a function outside a class, just as
you can now use self.var in the current function syntax. Before using
a function with .var, the global variable __self__ must be set.


I may be way off here, but I don't see how a global __self__ could work:

<not python>

class B:
def __init__(name,data):
.data = data*5
.name = '***%s***'%name

class A:
def __init__(name,data):
.data = data # __self__ is a?
.obj = B() # __self__ is now the B instance?
.name = name # now what?

a = A()

</not python>
Rich



Jul 18 '05 #29

P: n/a
> On Thu, 2004-05-13 at 17:59, David MacQuigg wrote:
[snip]
Ruby uses $var for instance variables.


Hi.

I realize this is not germane but Ruby uses @var for instance variables.
$var is used for globals.

Sean
Jul 18 '05 #30

P: n/a
David MacQuigg wrote:
James has been telling me it is terribly wrong to keep data as
attributes of classes


There's nothing inherently wrong with that, as long as
you're certain you will only ever want one instance of that
piece of data.

In my chip example, you might want to keep a count of the
total number of transistors in your chip as a class variable
of the Transistor class. But that only works if your program
only ever deals with one chip at a time. If there can be more
than one chip, then you'll need a Chip class whose instances
either keep a count of Transistors contained in that chip,
or have a method that can compute the count when needed.

Even if you think you will only deal with one chip at a
time, it would be good design practice to plan on having
a Chip class anyway, in case the requirements change. The
amount of work involved is about the same either way, and
the design which avoids class variables is more flexible.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #31

P: n/a
You know, it's interesting, I'm an engineer at a semiconductor
company, and I've written netlist manipulation objects over and over
again with only the most basic oop structure, and they usually work
fine. Associating data with each cell is usually not that hard,
although the show() function showing data from all cells above it (?)
seems kind of strange. This type of problem is a case where object
encapsulation is usually a lot more effective than inheritance. In my
netlist objects, the most basic object is just a name object, which
everything inherits because everything in a netlist has a name.
Beyond that, there's some container inheritance, I came up with an
ordered dictionary for pin data, some simple inheritance where Pin
objects inherit from Net objects, but that's about it.

The basic object structure is pretty simple. The highest level object
is a netlist. The netlist contains a list (just a simple python list)
of cell objects. The cell objects contain a list of nets, a list of
pins, and a list of instances. There are classes for instances, nets,
and pins (which inherit from nets). The instances do refer to their
parent cells, and the pins from the parent are copied in, but that's
about as complicated as things get in the class hierarchy. There's
also a special container class to describe the connections in a cell,
so I can send the connection mesh a net, an instance, or an
instance,pin tuple, and find out what it's connected to. That's
probably the most complicated class, since it uses a few internal
dicts and a type driven interface to resolve the input data. But
that's really about the size of it. Transistors would be instances in
cells (although a transistor cell parent is also in there) and gates
would simply be cells. Now if you had similar cells with different
current or power characteristics, I suppose you'd need to associate
those with an instance class instead of a cell class. Beyond that, I
don't really see why this is such a difficult thing to do. Unless
there's some other overriding goal this needs to accomplish that isn't
in the spec below. You might want to try just coming up with
something like the container hierarchy I have, not worrying about
inheritance, and see if that does the trick for you.


Greg Ewing <gr**@cosc.canterbury.ac.nz> wrote in message news:<2g************@uni-berlin.de>...
David MacQuigg wrote:
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.


It's still not clear whether the hierarchy you're talking
about is a hierarchy of *kinds* of cells (i.e. a class
hierarchy), or a physical hierarchy of actual cells (i.e.
an instance hierarchy).

Both of these hierarchies can exist at the same time, and
they are different.

For example, you might have a kind of cell called NandGate,
and a kind of cell called Transistor. An instance of NandGate
contains instances of Transistor -- this is an instance
hierarchy. But Transistor is not a subclass of NandGate --
that would imply a Transistor is a kind of NandGate, which
is not true.

On the other hand, NandGate may well be a subclass of Gate,
and Gate and Transistor may be subclasses of Component. But
that doesn't mean Gates *contain* NandGates.

If prototype-based programming leads to confusion between
class hierarchies and instance hierarchies, the I would
say it's not helpful.

Jul 18 '05 #32

P: n/a
David MacQuigg <dm*@gain.com> wrote in message news:<4k********************************@4ax.com>. ..
I'm beginning to understand what you are saying. Thanks for hanging
in there.

On 12 May 2004 20:51:20 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<q3********************************@4ax.com>. ..
On 10 May 2004 16:53:06 -0700, mo******@tcd.ie (James Moughan) wrote:

>David MacQuigg <dm*@gain.com> wrote in message news:<88********************************@4ax.com>. ..
>> On 8 May 2004 07:07:09 -0700, mo******@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

Jul 18 '05 #33

P: n/a
On 17 May 2004 12:35:23 -0700, co************@attbi.com (Corey
Coughlin) wrote:
You know, it's interesting, I'm an engineer at a semiconductor
company, and I've written netlist manipulation objects over and over
again with only the most basic oop structure, and they usually work
fine. Associating data with each cell is usually not that hard,
although the show() function showing data from all cells above it (?)
seems kind of strange. This type of problem is a case where object
encapsulation is usually a lot more effective than inheritance. In my
netlist objects, the most basic object is just a name object, which
everything inherits because everything in a netlist has a name.
Beyond that, there's some container inheritance, I came up with an
ordered dictionary for pin data, some simple inheritance where Pin
objects inherit from Net objects, but that's about it.

The basic object structure is pretty simple. The highest level object
is a netlist. The netlist contains a list (just a simple python list)
of cell objects. The cell objects contain a list of nets, a list of
pins, and a list of instances. There are classes for instances, nets,
and pins (which inherit from nets). The instances do refer to their
parent cells, and the pins from the parent are copied in, but that's
about as complicated as things get in the class hierarchy. There's
also a special container class to describe the connections in a cell,
so I can send the connection mesh a net, an instance, or an
instance,pin tuple, and find out what it's connected to. That's
probably the most complicated class, since it uses a few internal
dicts and a type driven interface to resolve the input data. But
that's really about the size of it. Transistors would be instances in
cells (although a transistor cell parent is also in there) and gates
would simply be cells. Now if you had similar cells with different
current or power characteristics, I suppose you'd need to associate
those with an instance class instead of a cell class. Beyond that, I
don't really see why this is such a difficult thing to do. Unless
there's some other overriding goal this needs to accomplish that isn't
in the spec below. You might want to try just coming up with
something like the container hierarchy I have, not worrying about
inheritance, and see if that does the trick for you.


This is the kind of thinking I like. 90% of what we need to do can be
done with the simplest of programming constructs. I know your company
probably considers what you develop proprietary, but would it be
possible for you to contribute an example suitable for teaching OOP to
EE students?

I will probably still keep the Animals example in the intro, since it
shows almost everything we will ever need in the simplest possible
structure. A netlister would be a great example, however, especially
since my students are studying EDA tools.

-- Dave

Jul 18 '05 #34

P: n/a
On Mon, 17 May 2004 16:40:00 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
David MacQuigg wrote:
James has been telling me it is terribly wrong to keep data as
attributes of classes


There's nothing inherently wrong with that, as long as
you're certain you will only ever want one instance of that
piece of data.

In my chip example, you might want to keep a count of the
total number of transistors in your chip as a class variable
of the Transistor class. But that only works if your program
only ever deals with one chip at a time. If there can be more
than one chip, then you'll need a Chip class whose instances
either keep a count of Transistors contained in that chip,
or have a method that can compute the count when needed.

Even if you think you will only deal with one chip at a
time, it would be good design practice to plan on having
a Chip class anyway, in case the requirements change. The
amount of work involved is about the same either way, and
the design which avoids class variables is more flexible.


This makes a lot more sense than a total ban on data in classes.

I think the generalization to a "multi-zoo" program probably belongs
in the third example, along with perhaps some techniques like James
showed to make the code more robust by generating the classes rather
than writing them. The first and second examples ( Animals_1 and
Animals_2 need to be very simple.

-- Dave

Jul 18 '05 #35

P: n/a
David MacQuigg <dm*@gain.com> wrote in message news:<gt********************************@4ax.com>. ..
On Mon, 17 May 2004 16:40:00 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
David MacQuigg wrote:
James has been telling me it is terribly wrong to keep data as
attributes of classes

[snip]
This makes a lot more sense than a total ban on data in classes.

Just to clarify, once again - I was not suggesting a ban on data in
classes - I was objecting to the specific way in which the data was
being passed around and used in the class heirarchy.
I think the generalization to a "multi-zoo" program probably belongs
in the third example, along with perhaps some techniques like James
showed to make the code more robust by generating the classes rather
than writing them. The first and second examples ( Animals_1 and
Animals_2 need to be very simple.

-- Dave

Jul 18 '05 #36

P: n/a
Well yeah, my code would be proprietary, but you can probably
reconstruct most of it from the description above. Just start with a
basic outline, add some interface functions on the front end to read
and write spice or verilog netlists, and see how it goes. (Starting
with spice would be a lot easier, it's a much easier format to parse.)
Maybe some of the other EE savvy people here would be willing to help
out. The only real problem with using it as an example is that it
isn't heavily object oriented. It uses some simple inheritance, some
classes, and some operator overloading, but if you want to get into
these complex ideas, it may not be the way to go.

If you're looking to make a functional system of some kind, a basic
netlist hierarchy would definitely be a good example. Given your
descriptions of current analysis, it sounds like you might be doing
something for power analysis. In the basic hierarchy I described, you
can imagine having extra power attributes attached to each cell, and
to get the total power you could easily write a little recursive
instance power summation method. If you want to do something more
complicated, like roll in table driven power data or state dependent
power, you'll need to overlay some sort of simulation state on top of
it, which sounds like it would involve adding state to each net.
Hmm.... if everything inherited from some timescale object, and some
basic functionality was added for transistor primitives, I suppose it
could work. But starting with a good netlist container would be a
great first step. So start with a netlist class that holds a list of
cells, a cell class that holds lists (or dicts) of nets, instances,
and ports, then fill in the instance, net and port classes, and you
should be well on your way.
David MacQuigg <dm*@gain.com> wrote in message news:<oe********************************@4ax.com>. ..

This is the kind of thinking I like. 90% of what we need to do can be
done with the simplest of programming constructs. I know your company
probably considers what you develop proprietary, but would it be
possible for you to contribute an example suitable for teaching OOP to
EE students?

I will probably still keep the Animals example in the intro, since it
shows almost everything we will ever need in the simplest possible
structure. A netlister would be a great example, however, especially
since my students are studying EDA tools.

-- Dave

Jul 18 '05 #37

P: n/a
On 18 May 2004 17:00:12 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<gt********************************@4ax.com>. ..
On Mon, 17 May 2004 16:40:00 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
>David MacQuigg wrote:
>> James has been telling me it is terribly wrong to keep data as
>> attributes of classes
>

[snip]

This makes a lot more sense than a total ban on data in classes.


Just to clarify, once again - I was not suggesting a ban on data in
classes - I was objecting to the specific way in which the data was
being passed around and used in the class heirarchy.


You have some good ideas, but you have been making extreme statements
and over-generalizing.
"""
5/17/04:
OK: "The whole idea of having these structures in any program is
wrong."

Firstly, the program uses a class hierarchy as a data structure. That
isn't what class heirarchies are designed for, and not how they should
be used IMO. But it's what any bright student will pick up from the
example.
"""

We could avoid a lot of non-productive discussion if you were more
careful with these statements. Instead of the above, you could say:
"""
Generally, its not a good idea to store *changeable* data in the class
itself. The Animals_2 program has variables like _numAnimals, which
change when any instances of subclasses are created. If this were a
much bigger program with hundreds of classes being implemented by many
different programmers, one of those programmers might forget to add a
call to the parent's __init__, and all counts of classes above the new
class would be wrong.
"""

Then we could have a productive discussion on whether, in this case,
_numAnimals is OK. I would argue yes, but I understand your point
about the possibility of new, incorrectly written classes causing a
problem.

The reasons I would argue yes are:
1) This is a very simple hierarchy, even if it grows to a hundred
classes.
2) The incrementing of the _num... variables is a simple and standard
part of every __init__ in this hierarchy. A programmer who would
ignore that, is a programmer who would completely mess up modification
of a more complex program involving automatic generation of classes.
3) This is not a database or transaction processing system that needs
to stay online for days at a time, and is vulnerable to out-of-sync
problems arising from interupted sessions, etc.
4) This is an example for students who are new to OOP. I would rather
keep the example simple, and point out how it might be improved, than
burden the students with all the details of a production-quality
program.

I've made your program, Animals_JM.py at
http://ece.arizona.edu/~edatools/Python/Exercises/ the next example
*after* Animals_2. The topic of techniques to make a program robust
and maintainable is worth a whole chapter, but it would be a
distraction in the introductory chapter.

I would still like to see a way to gain the benefits of both Animals_2
and Animals_JM in one program. My attempts to follow up on
suggestions in this discussion have only added complexity, not
improved the program.

-- Dave

Jul 18 '05 #38

P: n/a
On 17 May 2004 20:11:44 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<4k********************************@4ax.com>. ..

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.


The trade-off, in the case of _numAnimals is: Do we want a simple
program with instant display of this number, or a more complex program
that scans the entire hierarchy to re-create the total every time it
is needed.

There is an analogy in the netlisting programs used in circuit design.
Netlisting a big design is a time-consuming operation, so each cell is
netlisted when it is saved. When the design is simulated, there is a
quick check of the time-stamps on the entire hierarchy, to make sure
all of the incremental netlists are up-to-date. One could argue that
somehow the time-stamps could get out-of-sync (incorrect clock setting
on a client machine, etc.). Yet nobody says that not netlisting the
entire design every time is bad programming.

It's always a compromise. Robustness is never absolute.

-- Dave

Jul 18 '05 #39

P: n/a
David MacQuigg wrote:
There is an analogy in the netlisting programs used in circuit design.
Netlisting a big design is a time-consuming operation, so each cell is
netlisted when it is saved. When the design is simulated, there is a
quick check of the time-stamps on the entire hierarchy, to make sure
all of the incremental netlists are up-to-date.


This is a form of cacheing, which is a well-proven technique.
But it's an optimisation, and as such it should be the last
thing added to a design, and then only when it becomes necessary.

Also, there's a difference between cacheing and maintaining
redundant data representations. With a cache, if in doubt you
can always throw away the cache and re-calculate. But if your
design relies on incrementally keeping parallel data structures
up to date, if anything gets out of whack, you're hosed,
because you don't have the ability to re-calculate the
redundant data from scratch.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #40

P: n/a
On Thu, 20 May 2004 13:32:14 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:

<snip>
Also, there's a difference between cacheing and maintaining
redundant data representations. With a cache, if in doubt you
can always throw away the cache and re-calculate. But if your
design relies on incrementally keeping parallel data structures
up to date, if anything gets out of whack, you're hosed,
because you don't have the ability to re-calculate the
redundant data from scratch.


Seems to me there's a semantic problem here: if you can't re-calculate
it, then by definition it's not redundant.

-Kevin
Jul 18 '05 #41

P: n/a
David MacQuigg <dm*@gain.com> wrote in message news:<47********************************@4ax.com>. ..
On 17 May 2004 20:11:44 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<4k********************************@4ax.com>. ..
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.


The trade-off, in the case of _numAnimals is: Do we want a simple
program with instant display of this number, or a more complex program
that scans the entire hierarchy to re-create the total every time it
is needed.

There is an analogy in the netlisting programs used in circuit design.
Netlisting a big design is a time-consuming operation, so each cell is
netlisted when it is saved. When the design is simulated, there is a
quick check of the time-stamps on the entire hierarchy, to make sure
all of the incremental netlists are up-to-date. One could argue that
somehow the time-stamps could get out-of-sync (incorrect clock setting
on a client machine, etc.). Yet nobody says that not netlisting the
entire design every time is bad programming.

It's always a compromise. Robustness is never absolute.


All true; there are certainly times when you need to store complex
calculations, otherwise we'd all be programming in Haskell.

However, there are different ways to achieve the same result. Some
would be transparent and relatively easy to debug, like storing the
number at a container-instance level. The particular way it is done
in Animals_2 is, IMO, one of the less desirable alternatives,
especially as a pattern to teach students.

Jam

-- Dave

Jul 18 '05 #42

P: n/a
> >David MacQuigg <dm*@gain.com> wrote in message news:<gt********************************@4ax.com>. ..
On Mon, 17 May 2004 16:40:00 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:

>David MacQuigg wrote:
>> James has been telling me it is terribly wrong to keep data as
>> attributes of classes
> [snip]
This makes a lot more sense than a total ban on data in classes.

Just to clarify, once again - I was not suggesting a ban on data in
classes - I was objecting to the specific way in which the data was
being passed around and used in the class heirarchy.


You have some good ideas, but you have been making extreme statements
and over-generalizing.
"""
5/17/04:
OK: "The whole idea of having these structures in any program is
wrong."

Firstly, the program uses a class hierarchy as a data structure. That
isn't what class heirarchies are designed for, and not how they should
be used IMO. But it's what any bright student will pick up from the
example.
"""

We could avoid a lot of non-productive discussion if you were more
careful with these statements. Instead of the above, you could say:
"""
Generally, its not a good idea to store *changeable* data in the class
itself. The Animals_2 program has variables like _numAnimals, which
change when any instances of subclasses are created. If this were a
much bigger program with hundreds of classes being implemented by many
different programmers, one of those programmers might forget to add a
call to the parent's __init__, and all counts of classes above the new
class would be wrong.
"""


This wouldn't quite be my position, though. It doesn't hurt to have
changable data in a class; it's a question of what happens when you
loose the locality of that data, especially in terms of debugging
effort.

Quite likely I have not always communicated myself to you perfectly. I
see how someone could ignore the word heirarchy in the above quote and
loose most of the meaning, which was that data should generally not be
distributed throughout the class heirarchy as if it were a data
structure.

However, I had thought by now that I had made it clear that I do not
regard it as "terribly wrong to keep data as attributes of classes";

2004-05-10: "...you should think carefully before using classes to
store data"

"2004-05-10": "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..."

So I am not sure why you would misrepresent my position so thoroughly.

Then we could have a productive discussion on whether, in this case,
_numAnimals is OK. I would argue yes, but I understand your point
about the possibility of new, incorrectly written classes causing a
problem.

The reasons I would argue yes are:
1) This is a very simple hierarchy, even if it grows to a hundred
classes.
2) The incrementing of the _num... variables is a simple and standard
part of every __init__ in this hierarchy. A programmer who would
ignore that, is a programmer who would completely mess up modification
of a more complex program involving automatic generation of classes.
3) This is not a database or transaction processing system that needs
to stay online for days at a time, and is vulnerable to out-of-sync
problems arising from interupted sessions, etc.
4) This is an example for students who are new to OOP. I would rather
keep the example simple, and point out how it might be improved, than
burden the students with all the details of a production-quality
program.

And I would have argued no;

1) I'm not sure why this example is simpler than any other
singly-inherited heirarchy. IMO the way it works makes it more
complex than most I've seen.
2) If there's one thing you can rely on, it's people making mistakes,
especially the simple ones. Making a program easy to debug in the
face of simple mistakes is IMO an extremely good idea.
3) That's not especially relevant to what I've been talking about,
that is, programmer error and debugging.
4) Whatever you teach students the first time round takes a long, long
time to leave their heads, if it ever does. Picking practically and
theoretically sound examples would not be hard. These do not have to
be complex at all, just simple examples of typical uses of class
heirarchies.

I've made your program, Animals_JM.py at
http://ece.arizona.edu/~edatools/Python/Exercises/ the next example
*after* Animals_2. The topic of techniques to make a program robust
and maintainable is worth a whole chapter, but it would be a
distraction in the introductory chapter.

I would still like to see a way to gain the benefits of both Animals_2
and Animals_JM in one program. My attempts to follow up on
suggestions in this discussion have only added complexity, not
improved the program.

-- Dave


Simple method - choose a different example. However, you do not
appear to be open to this suggestion.
Jul 18 '05 #43

P: n/a
On 19 May 2004 23:26:38 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg wrote:
I've made your program, Animals_JM.py at
http://ece.arizona.edu/~edatools/Python/Exercises/ the next example
*after* Animals_2. The topic of techniques to make a program robust
and maintainable is worth a whole chapter, but it would be a
distraction in the introductory chapter.

I would still like to see a way to gain the benefits of both Animals_2
and Animals_JM in one program. My attempts to follow up on
suggestions in this discussion have only added complexity, not
improved the program.


Simple method - choose a different example. However, you do not
appear to be open to this suggestion.


If I were not open to suggestions, I would not be spending all this
time patiently extracting the good suggestions from this long thread,
and putting up with dogmatic statements and personal attacks. I could
repond by saying most of what I'm reading is hot air, but that would
be equally provocative. Let's see if we can bring this to a better
conclusion.

I like your example, but not as a *substitute* for Animals_2. It's
just too complex for non-CIS students at this point in the course. I
think I understand your concerns about Animals_2. I think the best
way to deal with those concerns is not by complicating the example, or
by taking out everything that could lead to problems, but rather by
pointing out the problems, and showing subsequent examples that fix
those problems, even at the expense of more complexity.

If you have a simple all-in-one alternative to Animals_2, show me.

Meanwhile, what I have understood as problems and suggested solutions
includes:
1) Class variables like numMammals that are not intended to be
directly altered by the user should be made private variables, by
adding a leading underscore. _numMammals.
2) Direct calls to a function from a parent class (super calls) might
not get updated if a programmer adds a new class in the hierarchy.
Calls like Animal.__init__(self) should be replaced by a general call
to the __init__ function from whatever class is directly above the
current class.
Mammal.__bases__[0].__init__(self)
Note: Calls like Mammal.talk(self), which really *are* intended to use
a function from a specific class, should be left as is.
3) Variables like _numMammals which depend on variables in other
classes could get out-of-sync with those other variables ( e.g. if a
programmer adds a new class and forgets to call __init__). These
variables could be replaced by functions which re-calculate the
dependent values from the original independent data.
Note: This is a trade-off of simplicity and efficiency vs robustness
against future programmer error.

I've implemented these suggestions in Animals_2c.py at
http://ece.arizona.edu/~edatools/Python/Exercises/

Here is a snippet from one of the classes:

class Animal(object):
_numAnimals = 0
def numAnimals():
return ( Animal._numAnimals +
Mammal.numMammals() + Reptile.numReptiles() )
numAnimals = staticmethod(numAnimals)

I've re-defined _numAnimals to hold only the count of Animal
instances. To get the total count of all animals, I've got a new
function numAnimals. This function does not walk the entire
hierarchy, as I suspect you might require, but it does add up the
totals from all subclasses directly below Animal. In each of those
subclasses, we define a function which totals the numbers from all of
its subclasses. In this way we get a simple recursive re-calculation
of all totals anywhere in the hierarchy, and the only actual data is
the count in each class of instances of that class.

We still have an inter-dependency problem when adding classes to the
hierarchy. We need to remember to add the appropriate num... function
for the new class to its parent class. For example, when we added a
Reptile class between Animal and Snake, we modified the function above
to include Reptile.numReptiles() in the total.

We also need to make sure we include *all* children of Reptile in the
new numReptiles function.

class Reptile(Animal):
-numReptiles = 0
def numReptiles():
return ( Reptile._numReptiles +
Snake.numSnakes() + Lizard.numLizards() )

Oops. We forgot Geckos !!

To avoid these errors in programming, we may need a function that
automatically searches for subclasses, and calls their num functions.
I'm not sure how to do this without making it excessively complex.
Suggestions will be appreciated.

-- Dave

Jul 18 '05 #44

P: n/a
On 19 May 2004 22:27:05 -0700, mo******@tcd.ie (James Moughan) wrote:
David MacQuigg <dm*@gain.com> wrote in message news:<47********************************@4ax.com>. ..
On 17 May 2004 20:11:44 -0700, mo******@tcd.ie (James Moughan) wrote:
>David MacQuigg <dm*@gain.com> wrote in message news:<4k********************************@4ax.com>. ..

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


The trade-off, in the case of _numAnimals is: Do we want a simple
program with instant display of this number, or a more complex program
that scans the entire hierarchy to re-create the total every time it
is needed.

There is an analogy in the netlisting programs used in circuit design.
Netlisting a big design is a time-consuming operation, so each cell is
netlisted when it is saved. When the design is simulated, there is a
quick check of the time-stamps on the entire hierarchy, to make sure
all of the incremental netlists are up-to-date. One could argue that
somehow the time-stamps could get out-of-sync (incorrect clock setting
on a client machine, etc.). Yet nobody says that not netlisting the
entire design every time is bad programming.

It's always a compromise. Robustness is never absolute.


All true; there are certainly times when you need to store complex
calculations, otherwise we'd all be programming in Haskell.

However, there are different ways to achieve the same result. Some
would be transparent and relatively easy to debug, like storing the
number at a container-instance level. The particular way it is done
in Animals_2 is, IMO, one of the less desirable alternatives,
especially as a pattern to teach students.


I'm still waiting for that one simple example that you say will do it
all, and do it right.

-- Dave

Jul 18 '05 #45

P: n/a
Kevin G wrote:
Seems to me there's a semantic problem here: if you can't re-calculate
it, then by definition it's not redundant.]


Obviously if it's redundant then in principle you can
always recalculate it. But if you don't plan for that,
you can end up not having any piece of code in the
system you can call to recalculate it. Approaching the
problem from the cacheing perspective at least ensures
that you do have such a piece of code.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #46

P: n/a
David MacQuigg wrote:
I'm still waiting for that one simple example that you say will do it
all, and do it right.


I don't think you should be trying to cram all the features
of OOP into a single example.

Also, I worry that the zoo topic will seem contrived. Why
not base your examples on some real problems from your
audience's subject area?

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Jul 18 '05 #47

P: n/a
On Fri, 21 May 2004 13:40:59 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
Kevin G wrote:
Seems to me there's a semantic problem here: if you can't re-calculate
it, then by definition it's not redundant.]


Obviously if it's redundant then in principle you can
always recalculate it. But if you don't plan for that,
you can end up not having any piece of code in the
system you can call to recalculate it. Approaching the
problem from the cacheing perspective at least ensures
that you do have such a piece of code.


Another good example is the redundant setup parameters in a circuit
design system. A setting to designate the location of output files
may start in some hidden setup file, get read in during startup, and
get copied to a number of internal variables in various tools that
have been "integrated" into the system. If you change that setting,
there is no way to propagate the changes through all of the tools.
You have to restart the whole system. Seems like there is an inverse
relationship between the price of software and the quality.

-- Dave

Jul 18 '05 #48

P: n/a
On Fri, 21 May 2004 13:45:52 +1200, Greg Ewing
<gr**@cosc.canterbury.ac.nz> wrote:
David MacQuigg wrote:
I'm still waiting for that one simple example that you say will do it
all, and do it right.
I don't think you should be trying to cram all the features
of OOP into a single example.


I agree. Digestible chunks is the right approach. Animals_1.py is
the first example of classes, including data, methods, instance
variables, and inheritance. Thats a pretty large chunk, but what
makes it digestible is the student's prior knowledge of modules,
functions, and all forms of data.

Animals_2a1.py (the current version) adds more levels to the
hierarchy, a special method __init__, static methods, bound and
unbound methods, the odd scoping rule for class variables, and
_private variables. This is all the basics that students will need if
they will be writing their own classes.

There probably won't be an Animals_3, but instead, "level 3" will be a
bunch of examples to illustrate various techniques, like the robust
programming that JM has been pushing, and maybe some topics from
chapters 21 - 23 of Learning Python.
Also, I worry that the zoo topic will seem contrived. Why
not base your examples on some real problems from your
audience's subject area?


Given the goals of the level one and two examples ( cover all the
basic features in two simple examples ) I think any example will be
contrived. The level 3 examples will be the right place to introduce
real circuit design problems.

-- Dave

Jul 18 '05 #49

P: n/a
I've completed a revision of my OOP chapter. See Prototypes.doc at
http://ece.arizona.edu/~edatools/Python The major change is adding
some footnotes pointing out the limitations of Animals_2, and adding a
section on Robust Programming, using your Animals_JM as an example.

Comments are welcome.

-- Dave
Jul 18 '05 #50

99 Replies

This discussion thread is closed

Replies have been disabled for this discussion.