Unification of Methods and Functions

David MacQuigg dmq at gain.com
Fri May 7 20:20:39 EDT 2004


On 7 May 2004 06:31:51 -0700, moughanj at tcd.ie (James Moughan) wrote:

>David MacQuigg <dmq at gain.com> wrote in message news:<knfl90tes9m608n99jn9lfcnpij5gqemaf at 4ax.com>...

>> I don't want to argue implementation details, as I am no expert, but I
>> think you are saying something is wrong at the user level, and that
>> puzzles me.
>> 
>> A global function, if I understand your terminology correctly, is one
>> defined at the module level, outside of any class.  Such a function
>> cannot have instance variables.  If you were to reference that
>> function from within a class, it would just act as a normal function
>> (a static method in Python terminology).  I can't see the problem.
>> 
>
>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.

Let me try to re-write this example as I think you intended:

def getLength(self): return self.length  # a global function

class Foo:
    length = 5

Foo.getLength = getLength

foo = Foo()

foolen = foo.getLength  # a bound method
FooLen = Foo.getLength  # an unbound method

print foolen()    #=> prints '5'
print FooLen(foo) #=> prints '5'

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.  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 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).  In the proposed syntax, global
functions and class methods would have the same calling sequence ( no
special first argument ).  If the method has instance variables, it
will use the global __self__ object, which was set to whatever
instance called the method.

Again, in Animals.py: Cat.show() has no instance variables, so it just
ignores __self__.  Cat.talk() has instance variables .name and .sound
-- so these are interpreted as __self__.name and __self__.sound


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

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

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

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

[snip a few paragraphs where we agree !! :>) ]

>> >I must note that Animals_2 is a total abuse of OOP techniques.  You
>> >continuously define classes to do the same thing, repeat the same code
>> >in each, then give them slightly different names.
>> 
>> This is a textbook introduction, not a real program.  The purpose of
>> the example is to show a complete OOP hierarchy in a small example,
>> with a good selection of the method styles most needed in a real
>> program.  The similarity between the show() methods in different
>> classes would not be so tempting to reduce to one global function if I
>> had made a larger example, with more radically different outputs from
>> each show function.  I thought that just changing one string in each
>> function would be enough to say "This function is different."
>
>OOP is a tool.  It's abstraction makes it tempting to create arbitrary
>structures as examples, but doing that looses any sense of the reason
>why you would use that tool.  That's why I see people with CS degrees
>who can throw around objects and heirarchies at will but who can't
>structure a simple program effectively.
> 
>> You are not the only one who had this reaction.  See my reply to Don
>> Cave above.  I guess I need to thow in a little more "meat", so that
>> experienced programmers don't get distracted by the possibility of
>> making the whole program simpler by taking advantage of its
>> regularities.  This is the same problem I've seen in many texts on
>> OOP.  You really can't see the advantages of OOP in a short example if
>> you look at it with the attitude -- I can do that much more easily
>> without classes.  It's when you get to really big complex hierarchies
>> that the advantages of OOP become clear.
>
>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).

>> >Also, your show method is IMO more than dubious.  show does not
>> >logically belong to Feline.  As a result you are using a class to
>> >display data about other classes to which it is not connected.  This
>> >is not OOP.
>> 
>> I thought this part was pretty clear.  The show() method at each level
>> calls the show() method one level up, then adds its own stuff at the
>> end.  Feline.show() calls Mammal.show(), which prints lots of stuff
>> characteristic of mammals, all in a format unique to the Mammal class.
>> Mammal.show() in turn calls Animal.show().  At each level we have some
>> unique display of characteristics.  The purpose is to have a call at a
>> particular level show all characteristics of the animal from that
>> level up.
>
>It is clear, just not a good idea.
>
>> >If I were teaching someone and they produced this structure then I'd
>> >go over the rationale with them and try to figure out where they went
>> >wrong in their planning of the program, and maybe go back and tutor
>> >them on the basic ideas of OOP.  I would not change the language to
>> >make it easier to do in future.  :)
>> 
>> Both responses I have on this are basically experts saying -- you can
>> solve this particular problem more easily by restructuring it.  I
>> should have been more clear.  Imagine that each of these classes and
>> methods is fully expanded with lots of parameters to make each one
>> unique.  Don't even think about re-structuring, unless you are trying
>> to tell me that the whole idea of having these structures in any
>> program is wrong.  That would surprise me, since this "animals"
>> example is a common illustration of OOP.
>>
>
>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.

>Secondly, it breaks the entire concept of OOP.  Objects are designed
>to be individual entities with side-effects restricted to their scope,
>in order to modularize programs.
>
>In this example, you use side effects from one class to influence the
>output of another; a bovine will end up influencing the output of a
>cat, for example.  And the effect in completely implicit.  As a
>result, someone who is introducing a mouse class can break another
>part of your system without the faintest idea that they are affecting
>it.  It's a fragile structure leading to the almost inevitable
>creation of the most intractable type of bug.
>
>The fact that Python makes it hard to do is *good*.

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?

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

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




More information about the Python-list mailing list