Unification of Methods and Functions

James Moughan moughanj at tcd.ie
Sat May 8 10:07:09 EDT 2004


David MacQuigg <dmq at gain.com> wrote in message news:<4a9o90pbu122npgf4m2hrgg04g2j0ic6ka at 4ax.com>...
> On 7 May 2004 06:31:51 -0700, moughanj at 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



More information about the Python-list mailing list