Unification of Methods and Functions

D

David MacQuigg

Again, clearly I have not communicated myself well - I mean a
*completely* different example to the entire Animals approach. The
difficulty with writing a good Animals-type example comes from the
things which it is trying to do, which aren't especially well
expressed by a class-heirarchy.

What I am trying to do in Animals_2 is provide the simplest, most
learnable example of Python OOP, which has at least one of every basic
structure the students will encounter in a real program. So I include
bound, unbound, and static methods, but not metaclasses or
descriptors.

Creating a good example like this is turning out to be more of a
challenge than I expected. I'm fairly happy with Animals_2, but I
appreciate the "maintainability" problems you have pointed out, and
have added footnotes accordingly. The examples in Learning Python are
too spread out in little pieces, and of course, no attention to the
issues you are concerned about. If you know of another place I can
look for a good all-in-one teaching example, I am interested.
Why not just take one of your earlier program examples, show the
students what it looks like when object-oriented, then extend the
class(es)?

As I've mentioned, I don't like my example *at all* for your teaching
purposes, and I'm not suggesting you use it. It was simply to
illustrate that there is another method of doing things, and what the
original problems were. (A good solution to the Animals 'problem'
would be simple, it just wouldn't demonstrate any of the things you
want to show.)

Then it wouldn't be a good solution. What I want to show is at least
one of every structure needed in a real program.

I've made your example one of the exercises. It solves a particular
problem, and it is worth showing.
Hmm, I think perhaps I like your original example better than this.
:-\ It's a bit like treating a cut throat with a tourniquet.

Searching the subclasses will AFAIK still require some kind of
metaprogramming, which is not too suitable for a 4-hour beginner
course on OO.

Most likely, we will not cover the solutions to the maintainability
problems, but we will mention them so students are aware. Electronic
design engineers are good at structured, hierarchical, robust design,
so I won't have to spend a lot of time on encapsulation, modularity,
maintainability, etc. They are well aware of the need for protection
circuits on all inputs. Pointing out the problems in a simplified
program example should be sufficient.

-- Dave
 
D

David MacQuigg

All I'm saying is that for most programs, the bound method form is way, way,
way more commonly used than is the unbound calling form.

I would agree if you cut out two of the "ways" in this sentence. :>)
Still this is like saying names that start with the letter 'q' are way
less common than all other names, so it is OK if we have a special
form for functions with such names. To take this analogy a step
further, I would say that the "no-q" programmers have somehow gotten
used to this restriction, and work around it without even thinking,
while new users find it annoying to have to go back and fix a few
functions because they didn't remember the "no-q" rule.
I don't disagree that they are necessary to make the language complete, but
over and over again you have posted examples of the form "here's a bound method
example, here's an unbound method example. They're different". Yes, they're
different, but presenting them that way makes it sound like you normally have
an equal mix of bound and unbound method calls, and that the difference is so
subtle that confusion abounds. Neither is true in practice.

I've never stated or implied that unbound methods are as common as
bound methods. Nor would I say that confusion abounds. The different
calling sequences are just an unnecessary complexity that trips up
some new users, especially those that don't have experience in some
other OOP language.
<sigh> I know what static methods are _for_, but I don't see what is
necessarily complex about them, nor how your proposed unification would help -
in fact, ISTM that it would make it much more confusing because both the
implementor and caller of the code would never be quite sure if the method was
a static one or not, and that's something that needs to be known by both
parties.

I think you have not understood the proposal. It is *not* necessary
in the new syntax to know the difference between a static and
non-static method. There *is* no difference. A "static method" (a
function without instance variables) simply ignores __self__.
Perhaps we both dislike the current staticmethod syntax of Python, but IMO
making a static method look the same as a non-static method seems like a very
wrong thing to do. They are quite different in purpose and construction, so to
me it doesn't make sense that they should look the same in implementation OR in
usage. See, to me it makes sense that there's no 'self' in a static method -
since there's no instance for it to refer to. Likewise, it makes sense to me
that there _is_ a self in a bound method, else member variable lookup would be
magical (certainly more magical than the fact that a bound method knows what
instance it belongs to).

The differences are an artifact of the syntax, not any fundamental
difference in purpose. Normal methods have a special first argument.
Static methods do not. That's all there is to it. That's the way I
think when I write a program, and the way I will minimize the
complexity for my students. I still don't know what "static" means in
this context, but it doesn't matter. It's just a wart I need to add
after I've written a method definition, if it happens that the method
needs to be called without 'self'.

If there is were some fundamental purpose to static methods, there
would be something they can do that can't be done with the proposed
simpler syntax? Can you think of any example?
Yes - I suggested the basis for your current solution. :)

And I greatly appreciate that suggestion. The statefile format is now
readable enough that we will avoid having to write our own parser. By
the way, I wasn't the only one who missed the obvious solution. An
experienced Python programmer was going down the wrong path with me.
I'd say that first and foremost, it's powerful, and then simple. The example
IMO shows an abuse of the power without a good reason to do so (my reaction to
the example as "you're doing something odd for no good reason, and you get odd
behavior, so what?"). The proposed unification does not seem to take into
account the benefits of the current design - whether by choice or ignorance I
don't know.

I may be wrong, but it looks to me like there *are* no benefits of the
current binding syntax over the proposed syntax.

I don't know the reason behind the "Method Binding Confusion" example,
so I would hesitate to tell someone they don't need to do that. I
assume it was distilled from a much larger program. It does look
simple enough that I can imagine someone stumbling upon it in the
course of writing an ordinary program. In any case, the proposed
syntax can do this without any special effort.

What are the benefits of the current design? What can you do in the
current syntax that can't be done more easily in the proposed syntax?
Show me some code, and I will show you the equivalent in "Python 3".
Yes it has some value, but more than that: it fits the brain quite well. IMO it
adheres to the principle of least surprise,

The Principle of Least Surprise has always been one of my favorites.
It's a shame this principle offers us so little guidance in resolving
a simple issue like this unification question. It seems to be leading
us in opposite directions. I think the difference may be in our
backgrounds. You seem quite comfortable with Java, so I would guess
these different method forms fit with what you have learned there.

I'm coming at it from a different direction -- no CIS background at
all. I see more beauty, more simplicity, and less surprise in having
just one form of function/method -- unless there is something these
various method forms can do, that I can't do with a simpler syntax,
then I would change my mind, and *highlight* the differences with
keywords like "staticmethod" and "lambda". With the new syntax, I
wouldn't even use the word "method".
if you are really thinking about
what is going on (and this is why I haven't found it to be a problem for
newbies, because they usually _aren't_ that interested in precisely what is
going on). And if you're not really thinking about what is going on, it places
a pretty minimal burden on you - there's only a handful of rules a blissfully
ignorant programmer needs to adhere to, and that's pretty amazing. But once you
do understand what is going on, then you can do some pretty powerful things
that you can't do in e.g. Java (at least not directly and not without a lot of
work).

I need to feel like I understand what is going on. I'm not
comfortable with a bunch of arbitrary rules, especially if I
frequently run into cases where forgetting the rules can cause a
problem. With the proposed syntax, I understand binding completely.
I don't even see any wierd little edge cases, like the example from
the "binding confusion" thread. In that example, it should not matter
if we are calling a normal function, a built-in function, a normal
method, or a static method. If the function contains instance
variables ( with a leading dot ), it will look for __self__. If that
variable is not set, you will get an exception. That's all there is
to binding. It doesn't even matter if the function is defined inside
a class, or at the module level. That choice can be determined by
whatever is the best organization of the program.
Maybe, but I'm not arguing that the way things are is the way things have to
be - I'm arguing that (1) the current way is better than all other proposals to
date and (2) the specific proposal you've put forward introduces more problems
than it solves and possibly results in a less powerful language (but I'm not
sure because I haven't seen examples of how all calling use cases would be
affected).

I need specific examples of problems introduced by the new syntax, or
of something the old syntax can do better. We can argue general
principles forever, but one good example will settle this question.

Some of the highly-touted benefits of Prothon, I have concluded are
bunk, and I have eliminated them from my proposal. The total
elimination of all class-like behavior (getting rid of the "two-tier"
class/instance object model) is a case in point. In most programs,
two tiers are very useful. Nobody has even offered a use-case for the
'one-tier" object model of Prothon. As far as I can tell, none of the
"one-tier" advocates have even looked at Michele Simionato's
implementation of prototypes in Python. (comp.lang.python 4/28/04).

In spite of the bunk, we should not dismiss all the ideas in Prothon.
In my opinion, this unification of methods and functions is the one
gem we can distill from the mess. We can learn a lot by looking at
how languages designed *after* Python have tried to improve on it.
problems

But has Mark told you that his goal was brevity? I bet you could cut the pages
in half without ANY changes to the language, not because he did a poor job (he
obviously didn't) but because you have different goals in mind.

I assume his goal, like mine, is to teach basic Python with the
minimum time and effort. Brevity is not a goal in itself. There is
an optimum number of pages to explain a given set of topics, and I
think Learning Python is pretty close to that optimum. The reduction
in pages has to come from a reduction in the number of topics.
Nobody is taking that stance, as far as I can tell.

Nobody seems to understand that lambda functions, static methods, etc.
are clutter.

-- Dave
 
J

James Moughan

David MacQuigg said:
What I am trying to do in Animals_2 is provide the simplest, most
learnable example of Python OOP, which has at least one of every basic
structure the students will encounter in a real program. So I include
bound, unbound, and static methods, but not metaclasses or
descriptors.

Creating a good example like this is turning out to be more of a
challenge than I expected. I'm fairly happy with Animals_2, but I
appreciate the "maintainability" problems you have pointed out, and
have added footnotes accordingly. The examples in Learning Python are
too spread out in little pieces, and of course, no attention to the
issues you are concerned about. If you know of another place I can
look for a good all-in-one teaching example, I am interested.

Just for the sake of example, this is one of the programs which I
always write while learning a language, written in a way I'd use to
demonstrate OO; all it does is read in a wordlist, then print out
synonyms for user-inputed words. Handy for crosswords. ;-)

First in a data/tool centric style, which could be one of the earlier
examples:

def addItem(dict, key, elem):
if dict.has_key(key):
dict[key].append(elem)
else:
dict[key] = [elem]

def sortWord(word):
word = map(str.lower, word)
word.sort()
return ''.join(word)

def main():
multi_dict = {}

for line in open("wordlist.txt").readlines():
line = line.strip()
addItem(multi_dict, sortWord(line), line)
while True:
word = sortWord(raw_input('Enter a word to find synonyms'))
if multi_dict.has_key(word):
print multi_dict[word]
#lis = multi_dict[word]
#lis.append('blah') <- error!!
else:
print 'No synonyms in the worlist.'

main()

Not a bad solution, but inflexible. Also a program which repeats the
pattern in the commented-out lines will cause problems. So, we really
want to encapsulate the data in a class;

class Multi_dict:
def __init__(self):
self._dict = {}
def add(self, key, elem):
if self._dict.has_key(key):
self._dict[key].append(elem)
else:
self._dict[key] = [elem]
def get(self, key):
if self._dict.has_key(key):
return list(self._dict[key])
return []

def sortWord(word):
word = map(str.lower, word)
word.sort()
return ''.join(word)

def main():
multi = Multi_dict()

for line in open("wordlist.txt").readlines():
line = line.strip()
multi.add(sortWord(line), line)
while True:
word = sortWord(raw_input('Enter a word to find synonyms: '))
syns = multi.get(word)
if len(syns) > 0:
print syns
else:
print 'Sorry, no synonyms'

main()

This is a generalised solution to making a multi-entry dictionary.
However, we still need to call sortWord, which is a shame; someone
might try using two different functions to hash the elements, and it
makes our code sloppier.
However, if we put it into a class sortWord doesn't really need a self
attribute; so we make it a static method. We also extend the
Multi_dict class to avoid replicating the code.

class Multi_dict:
def __init__(self):
self._dict = {}

def add(self, key, elem):
if self._dict.has_key(key):
self._dict[key].append(elem)
else:
self._dict[key] = [elem]

def get(self, key):
if self._dict.has_key(key):
return list(self._dict[key])
return []

class Thesaurus(Multi_dict):
def __init__(self):
Multi_dict.__init__(self)

def sortWord(word):
word = map(str.lower, word)
word.sort()
return ''.join(word)
sortWord = staticmethod(sortWord)

def add(self, elem):
Multi_dict.add(self, self.sortWord(elem), elem)

def get(self, elem):
return Multi_dict.get(self, self.sortWord(elem))

def main():
theo = Thesaurus()

for line in open("wordlist.txt").readlines():
line = line.strip()
theo.add(line)
while True:
word = raw_input('Enter a word to find synonyms: ')
syns = theo.get(word)
if len(syns) > 0:
print syns
else:
print 'Sorry, no synonyms'

main()

Exercises for students: (i.e. stuff I was too lazy to do)
1) Modify the original program to terminate on an empty input line
2) Extend Multi_dict to create a class which stores only one example
of each item. (Class heirarchy.)

I think this shows everything you want in simple way, with the
exception of class-level data - you can show that by moving _dict to
class level, demonstrating a system which pools all of the words
entered into any Thesaurus, and point out the advantages/disadvantes
of this - there may be examples where this would be just what you
want.

You could pick an example from an area which your students are
familiar with to make whings more concrete.
 
D

Donn Cave

Quoth David MacQuigg <[email protected]>:
| On Sat, 22 May 2004 18:28:33 -0000, "Donn Cave" <[email protected]>
| wrote:
[1 page intro to Python OOP]
....
| This is OK for the first example. I would leave the __init__ methods
| to the second example, but either way it will take about 8 pages to
| comfortably explain OOP ( maybe ten if I include the "robust
| programming" examples that JM says I must ). I would like students to
| understand Python at the level they can follow what is going on in a
| real program, and maybe write a few classes themselves.

That 1 page is enough. The other 7 pages should be elaboration of
the language elements introduced on the 1st page. That will definitely
work for real programs. It is possible to write real programs in
idiomatic Python!

My first (and only) programming course was a half quarter of FORTRAN,
for numerical and statistical data processing in natural sciences.
The instructor chose to omit a number of features, including formatted
output if I remember right. FORTRAN is not a complicated language, and
formatted output is really a mainstay. Later I learned formatted output
on my own before I really went very far with programming, but I didn't
have to wrestle with it while learning the language. Good idea, reduce
initial exposure to the really minimum essentials.

|> Then I would go over that, showing what happens and why, until the
|> concepts introduced above seem to be clear for everyone. That would
|> conclude my treatment of classes. As an elementary language, there
|> are some slightly hard things to learn about Python, but this isn't
|> going to be one of them unless you make it hard.
|
| If you are saying we can totally ignore the different method forms, I
| think you are wrong. Bound and unbound methods, for example, will be
| needed in almost any sizable program. The need for static methods
| will arise when the student first writes a method that needs to work
| without a current instance.

I have never needed to write a method that works without a current
instance. Never, in a decade of using Python.

| The example I showed is not intended to explain method binding, and I
| would not use it in an introduction to OOP. It probably won't even be
| "stumbled upon" in a normal program. I posted it only to show that
| even experts can get confused by Python's binding syntax. Are you not
| confused by this example?

No, but by the time such a thing would have occurred to me at all -
to poke a function into a class via other means than "def" - I had
a fairly solid ground in the mechanics of the language. I may have
struggled with it, may have even experienced some frustration, but
not exactly confusion or profound surprise.

The very fact that you have set out to teach people a language that
you find confusing and nonsensical, when the language has a devoted
following and is wildly popular with new users in particular, should
give you pause. Do you want your students to end up like you?

Donn Cave, (e-mail address removed)
 
J

James Moughan

Just for the sake of example, this is one of the programs which I
always write while learning a language, written in a way I'd use to
demonstrate OO; all it does is read in a wordlist, then print out
synonyms for user-inputed words.
[snip]

Obviously the word 'synonym' should be replaced with 'anagram' in this
post. :) Never give your classes cute names like Thesaurus then post
before breakfast...
 
D

David MacQuigg

Quoth David MacQuigg <[email protected]>:
The very fact that you have set out to teach people a language that
you find confusing and nonsensical, when the language has a devoted
following and is wildly popular with new users in particular, should
give you pause. Do you want your students to end up like you?

To make a statement like this, you must have completely misunderstood
everything I have been saying. Goodbye, Mr. Cave. I'm sure we both
have better things to do with our time than start a flame war.
 
G

Greg Ewing

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

But I don't think you should be trying to cover all the
features of OOP before showing any real examples. That
approach seems almost guaranteed to lose people's interest,
the way you criticised supplier-part examples for.

Wouldn't it be better to do things the other way around?
Pick some (simplified if need be) examples based on
real-world problems, and, one by one, show how the
features of OOP can be used to solve them.
 
G

Greg Ewing

David said:
import math

def mypow(x, y):
return x**y

class MathA:
pow = math.pow

class MathB:
pow = mypow

ma = MathA()
mb = MathB()

print ma.pow(2,4) #=>
16.0
print mb.pow(2,4) #=>
# TypeError: mypow() takes exactly 2 arguments (3 given)

How would you explain this to non-CIS students in a class on circuit
design, where there is very little time to discuss programming?

I wouldn't have to, because I would never show them any code
that did anything as screwy as this in the first place, and
it's extremely unlikely they would stumble upon it themselves
in their first few hours of using Python.

To put it another way, this is not the sort of thing you
do in the normal course of writing Python code. It's an
example contrived specifically to be confusing, not the
sort of thing you'd use in a teaching course.
 
G

Greg Ewing

David said:
The need for static methods
will arise when the student first writes a method that needs to work
without a current instance.

But why would he need such a method, as opposed to
a plain function?
 
G

Greg Ewing

David said:
I can't comment on the usage statistics you cite, but it seems to me
that unbound methods are more important than these statistics would
imply. ... Learning Python also treats unbound methods as
much more "normal" than your argument would imply.

The only time you really need to deal with unbound methods
is when making super calls, and that accounts for maybe
1% or less of all method calls in the code I write.

I haven't seen what Learning Python says about unbound
methods, but if it puts any more emphasis on them than
this, it's putting too much emphasis on them.
Sure, we could move the load function
outside of the Bag class, but that would disrupt the natural structure
of the program. The load function is unique to Bags, and it belongs
in the Bag class.

The Pythonic way to handle this is to create a module
to hold the Bag class and any functions related to it,
e.g.

# module bags

class Bag:
...

def load(infile):
...

# in some other module
import bags
my_bag = bags.load("my_settings.ini")
Seems like my challenging statement was misleading. I have no
intention of bringing up strange binding problems in an introductory
class. This was a challenge to those who think that Python's binding
syntax is simple.

But seeing as your example did something you're not meant to
be doing in the first place, I don't see how any conclusions
can be drawn from it about simplicity or complexity in
everyday programming.
 
G

Greg Ewing

David said:
Nobody seems to understand that lambda functions, static methods, etc.
are clutter.

On the contrary, lambda has long been regarded as a wart
for various reasons. The best way to fix it is probably
to just drop it altogether. Nothing essential would be
lost.

We don't see static methods as clutter, because for the
most part we don't see them (i.e. we don't normally use
them). It would be no great loss if they were dropped,
too.

In neither case is it necessary to fundamentally
redesign the language to fix these, even if
they are regarded as problems.
 
D

Dave Brueck

David said:
I would agree if you cut out two of the "ways" in this sentence. :>)

Well, for kicks I looked at the standard Python library, since I figured it
would have the greatest concentration of unbound method use (my reasoning was
that since it was a library more than a consumer of a library, you'd have lots
of class hierarchies and therefore more occurences of unbound method calls to
super classes. Even in this situation it appears that unbound method calls are
less than 2% of all method calls. The real point is simply that unbound method
calls are pretty rare.
Still this is like saying names that start with the letter 'q' are way
less common than all other names, so it is OK if we have a special
form for functions with such names.

Not at all - that's a flawed analogy because variable names can be pretty
arbitrary and interchangable. The different types of function calls, method
invocations, whatever, are neither arbitrary nor interchangable.
I've never stated or implied that unbound methods are as common as
bound methods. Nor would I say that confusion abounds. The different
calling sequences are just an unnecessary complexity that trips up
some new users, especially those that don't have experience in some
other OOP language.

I'm beginning to understand the real root of the disagreement - you see no
value in the differences and therefore see the existence of the differences as
a flaw. But that's just the problem: these are not merely syntactic
differences, but they are semantic - they are different in purpose and in use,
so your suggestion to get rid of them seems very unappealing: from my
perspective you are removing something useful from the language to solve a
"problem" that I've yet to encounter, or seen anyone else encounter (including
new users of the language).
I think you have not understood the proposal. It is *not* necessary
in the new syntax to know the difference between a static and
non-static method. There *is* no difference. A "static method" (a
function without instance variables) simply ignores __self__.

And this is why the proposal seems so lousy: the difference between a static
and a non-static method is _not_ just a syntactic difference, their existance
is not _caused by_ the syntax. The syntax merely reflects that they are
different things to begin with. They are different tools, different features -
you use them for different things, so making them appear identical to the
maintainer of the code and the caller of the code seems like a really bad
idea - a maintenance nightmare. If you remove any syntactic difference, then
that information is lost from the code itself. But since that info is useful,
people will instead track that info in comments or something - yuck.
The differences are an artifact of the syntax, not any fundamental
difference in purpose. Normal methods have a special first argument.
Static methods do not. That's all there is to it.

No, no, no - they are different tools, plain and simple. The difference just
isn't skin deep - changing the syntax won't make them go away, it'll instead
make the language more ambiguous. Without meaning to be offensive, I must ask:
how much have you actually used any of these features you're interesting in
consolidating? (static method, unbound methods, bound methods, etc.)
If there is were some fundamental purpose to static methods, there
would be something they can do that can't be done with the proposed
simpler syntax?

That's just my point: after consolidating the syntax, static methods will still
exist, but there will be no way of knowing which ones are static and which ones
aren't _without reading every line of the code_ - there will be no syntactic
way to express the idea. Egads!
I may be wrong, but it looks to me like there *are* no benefits of the
current binding syntax over the proposed syntax.

Yes, I understand that it looks that way to you, but why is it that so many
people who have used the language for a long time disagree with you? Is it
because everyone else has blinders on? Could it be _possible_ that these
features have value and/or that the solution you propose is worse than what
currently exists? Again, how much did you use the language before coming to the
conclusion that you have?
What are the benefits of the current design?

I guess my real gripe with the proposal is that it takes what is currently
clear and makes sense and combines it in such a way to make it ambiguous to
everyone involved with the code. The features you propose consolidating really
are different in real programs, and making them all look the same just for the
heck of it doesn't seem to make much sense.
What can you do in the current syntax that can't be done more easily in the
proposed syntax?

Understand my code 6 months from now. Modify the code with less risk.
Understand the level of intent in someone else's code I'm using.
Show me some code, and I will show you the equivalent in "Python 3".

No, that burden is on your shoulders, as everyone not in favor of your
suggestions can let the thread die off and nothing will change. <0.5 wink> In
another thread I suggested you take the time to learn what the 2 or 3 most
common uses are for each of the features that will be affected, and show both
the implementation and use in current Python and after your proposed changes.
Not only will doing this give a clear example to everyone, it'll also show that
you are at least aware of how the different function/method forms are used
today in _real programs_ and not contrived examples - after re-reading some of
your comments I have my doubts.
The Principle of Least Surprise has always been one of my favorites.
It's a shame this principle offers us so little guidance in resolving
a simple issue like this unification question.

All of those guiding principles are subjective, but IMO it's pretty clear here
:)
us in opposite directions. I think the difference may be in our
backgrounds. You seem quite comfortable with Java, so I would guess
these different method forms fit with what you have learned there.

I abhor Java, actually, and I'm not even sure how to do the bound method form
in Java (or C++, which is where my background lies more than Java).
I need to feel like I understand what is going on. I'm not
comfortable with a bunch of arbitrary rules

Please understand that these are not arbitrary. The fact that you think they
are arbitrary makes me wonder if you really understand them. Don't be offended!
especially if I
frequently run into cases where forgetting the rules can cause a problem.

What I like about the current syntax, is that you don't really need to memorize
any rules because how you call them is pretty obvious, unless of course you're
not thinking about what you're doing, in which case there are bigger problems
at hand. For example, when I learned Python I never came across some tutorial
showing how to do an unbound method call - I got to the point where I needed
it, I tried the most obvious thing, and it worked. Likewise, the first time I
needed to get a reference to a bound method, I was impressed that the first
thing I tried worked (impressed with the language, not with myself :) ).
I need specific examples of problems introduced by the new syntax, or
of something the old syntax can do better. We can argue general
principles forever, but one good example will settle this question.

Like I said, if you really are interested in making headway with your proposal,
*you* need to demonstrate through some non-contrived examples that you
understand how things work today. I've read the pages on your site and either
they don't show all cases that will be affected, or the examples are too buried
by other changes so I missed them. So, show a few examples of how bound methods
are implemented and used today in Python, and how they'd be used and
implemented the new way. Show a few examples of how unbound methods are
implemented and used today, etc. Just showing one example and stating that
everything will be like that glosses over the fact that these things are all
used in very different ways today, and leaves the reader wondering if you have
really thought things through.
We can learn a lot by looking at
how languages designed *after* Python have tried to improve on it.

Look at the changes to Python, especially over the past few years - many of the
features are publicly credited as having come from other languages.

-Dave
 
D

David MacQuigg

Just for the sake of example, this is one of the programs which I
always write while learning a language, written in a way I'd use to
demonstrate OO; all it does is read in a wordlist, then print out
synonyms for user-inputed words. Handy for crosswords. ;-)

<snip examples>

I've posted these examples on our webpage at
http://ece.arizona.edu/~edatools/Python/Examples Let me know if I
should make any changes. These will be very helpful for students to
see alternative styles in programming.

-- Dave
 
J

James Moughan

David MacQuigg said:
<snip examples>

I've posted these examples on our webpage at
http://ece.arizona.edu/~edatools/Python/Examples Let me know if I
should make any changes. These will be very helpful for students to
see alternative styles in programming.

-- Dave

*Shakes head* OK, one change; please change the word synonyms to
anagrams in the text of the programs as well. :) When I write a
program which can algorithmically detect synonyms it'll be really
something...
 
B

Bengt Richter

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.


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.
If you take some wood and saw off pieces to make a box
with some fasteners, and then you put the leftover pieces
of wood *in* the box, you have pieces of wood with two different
kinds of relationship to the box.

I suspect reflecting on this distinction in terms of your examples
might be fruitful.

I can't spend the time to participate more than stolen moments right now,
so I haven't seen all of the thread. Sorry if I am rehashing something.

Regards,
Bengt Richter
 
D

David MacQuigg

Well, for kicks I looked at the standard Python library, since I figured it
would have the greatest concentration of unbound method use (my reasoning was
that since it was a library more than a consumer of a library, you'd have lots
of class hierarchies and therefore more occurences of unbound method calls to
super classes. Even in this situation it appears that unbound method calls are
less than 2% of all method calls. The real point is simply that unbound method
calls are pretty rare.

I appreciate your taking the time to actually research this question.
The 2% number for unbound functions doesn't surprise me. It would be
interesting if we could get similar statistics on static methods, but
I suspect the numbers would not be meaningful, given that the
staticmethod syntax is relatively new, and there is a long tradition
in Python of using various alternatives to static methods. New users
tend to copy traditions, which is OK, except that learning those
traditions is time-consuming.

To me, the real question is how often will my kind of users (no prior
experience with Python traditions or OOP, just writing what "comes
natural") - how often will they need a static method? That depends on
the size and complexity of the programs they are working with. My CDP
program will be fairly large, and I am using static methods without
hesitation. I would guess half the programs they will encounter would
have a static method here and there.
Not at all - that's a flawed analogy because variable names can be pretty
arbitrary and interchangable. The different types of function calls, method
invocations, whatever, are neither arbitrary nor interchangable.

Here is a better analogy than my "no-q" functions. I will use the
term "global method" to mean any method containing a global variable.
We now have "normal methods", "static methods", "global methods", and
a few others. There is no difference in the calling sequence or the
header line between a global method and a normal method. Yet we don't
seem to have any need for a statement like
myMethod = globalmethod(myMethod)
I'm beginning to understand the real root of the disagreement - you see no
value in the differences and therefore see the existence of the differences as
a flaw. But that's just the problem: these are not merely syntactic
differences, but they are semantic - they are different in purpose and in use,
so your suggestion to get rid of them seems very unappealing: from my
perspective you are removing something useful from the language to solve a
"problem" that I've yet to encounter, or seen anyone else encounter (including
new users of the language).


And this is why the proposal seems so lousy: the difference between a static
and a non-static method is _not_ just a syntactic difference, their existance
is not _caused by_ the syntax. The syntax merely reflects that they are
different things to begin with. They are different tools, different features -
you use them for different things, so making them appear identical to the
maintainer of the code and the caller of the code seems like a really bad
idea - a maintenance nightmare. If you remove any syntactic difference, then
that information is lost from the code itself. But since that info is useful,
people will instead track that info in comments or something - yuck.


No, no, no - they are different tools, plain and simple. The difference just
isn't skin deep - changing the syntax won't make them go away, it'll instead
make the language more ambiguous. Without meaning to be offensive, I must ask:
how much have you actually used any of these features you're interesting in
consolidating? (static method, unbound methods, bound methods, etc.)

I'm new to Python and object-oriented programming. I make no claim of
language expertise or vast programming experience. I'm simply
offering a perspective on Python from a technical professional
(circuit design engineer) who intends to use it as the basis for a new
design platform, and intends to recommend it to students and other
technical professionsls who could benefit greatly, but don't have time
to learn the language as well as they should.
That's just my point: after consolidating the syntax, static methods will still
exist, but there will be no way of knowing which ones are static and which ones
aren't _without reading every line of the code_ - there will be no syntactic
way to express the idea. Egads!


Yes, I understand that it looks that way to you, but why is it that so many
people who have used the language for a long time disagree with you? Is it
because everyone else has blinders on? Could it be _possible_ that these
features have value and/or that the solution you propose is worse than what
currently exists?

Saying everyone has blinders is too harsh. It's more like the
particular group of users in this discussion are more advanced than
the users I am concerned about, and the lack of unity in
function/method syntax is a minor issue they have "sailed past"
without even thinking.

I have a similar reaction to problems that bother less technical
users, like the need for a special operator to indicate "floor
division" or the problems with storing decimal fractions in a binary
floating-point number. These all seem pretty obvious to me, yet I
appreciate that Python makes an effort to accomodate these "naive"
users.
Again, how much did you use the language before coming to the
conclusion that you have?
see above
I guess my real gripe with the proposal is that it takes what is currently
clear and makes sense and combines it in such a way to make it ambiguous to
everyone involved with the code. The features you propose consolidating really
are different in real programs, and making them all look the same just for the
heck of it doesn't seem to make much sense.

proposed syntax?

Understand my code 6 months from now. Modify the code with less risk.
Understand the level of intent in someone else's code I'm using.

OK, here is what I can extract from all this: Having a separate
syntax for static methods is beneficial because it tells the
programmer at a glance that he can call a method from a class without
having an instance of that class at hand.

I can see this as a fundamental advantage of the current syntax, but I
would still question the value of this advantage, assuming that static
methods are rare. I would still opt for the unified syntax, and
accept the burden on rare occasions of having to scan the body of a
method (not "read every line") looking for the presence of instance
variables. Perhaps this is why Ruby highlights them with @. I think a
bare leading dot is pretty obvious, but now we're getting down to
personal preference.

If the need to quickly determine if a method is "static" is really an
issue, I would say, don't muck with the standard calling sequence, but
put a special "decorator" on the header line. I think that is being
proposed in PEP 318. Having this decorator could provide the same
fundamental benefit as the statement myMethod =
staticmethod(myMethod), and this does not require a special first
argument.
No, that burden is on your shoulders, as everyone not in favor of your
suggestions can let the thread die off and nothing will change. <0.5 wink> In

You left out a piece of the prior discussion. You said that I was
either ignorant or choosing to ignore the benefits of the current
syntax. My response was "show me" such a benefit. Now you seem to be
saying the burden is on me to show I have thought of everything.
Obviously, I can't do that. Equally obvious is that you could show me
one example of what you are talking about and prove your point.

I think if we both forget about trying to win an argument, and dig for
the facts wherever they may lead, we can avoid these deadlocks.
another thread I suggested you take the time to learn what the 2 or 3 most
common uses are for each of the features that will be affected, and show both
the implementation and use in current Python and after your proposed changes.
Not only will doing this give a clear example to everyone, it'll also show that
you are at least aware of how the different function/method forms are used
today in _real programs_ and not contrived examples - after re-reading some of
your comments I have my doubts.

I have shown the 4 function/method forms I'm aware of, and how to
translate them to a simpler syntax, in Appendix 1 of Prototypes.doc at
http://ece.arizona.edu/~edatools/Python Have you read that?

Again, we can avoid deadlock by focusing on the facts in front of us
and avoiding the argument "I'm a better expert than you, so I win."
We'll either get to the bottom of an issue, or agree that its a matter
of personal preference.
All of those guiding principles are subjective, but IMO it's pretty clear here
:)
Equally clear from my perspective.
I abhor Java, actually, and I'm not even sure how to do the bound method form
in Java (or C++, which is where my background lies more than Java).


Please understand that these are not arbitrary. The fact that you think they
are arbitrary makes me wonder if you really understand them. Don't be offended!

Again, you deleted the context. This is in response to your statement
that there are only a handful of rules a beginner needs to adhere to.
I do understand most of these rules (all but the strange example
earlier in this thread). However, I would like to eliminate *some* of
them, and keep just one: If you call a function from an insstance, you
get a bound function. Otherwise all references to the function are
unbound. Even the strange example will do exactly what you expect.

Prothon, by the way, eliminates even this rule, following a little too
rigidly the dictum "explicit is better". Their so-called explicit
binding syntax is a mess. I much prefer Python's implicit syntax.
"Practicality beats purity."
What I like about the current syntax, is that you don't really need to memorize
any rules because how you call them is pretty obvious, unless of course you're
not thinking about what you're doing, in which case there are bigger problems
at hand. For example, when I learned Python I never came across some tutorial
showing how to do an unbound method call - I got to the point where I needed
it, I tried the most obvious thing, and it worked. Likewise, the first time I
needed to get a reference to a bound method, I was impressed that the first
thing I tried worked (impressed with the language, not with myself :) ).

It didn't go quite as smoothly for me, probably because I was rushed
on my other projects, and also because I had no prior experience with
OOP. I learned about bound and unbound methods by reading Learning
Python, but it didn't really sink in until I started using them. I
don't expect my students to learn these topics in the short time we
have in class. Most of them will not be writing complete programs, so
I don't expect them to pick it up later, either. Most of them will be
modifying scripts that have been written by someone with more
expertise in Python. A working knowledge of Python up to, but not
including, the OOP chapters is sufficient for most.

My interest in OOP is not only for what it can do in my own programs,
but I see Python OOP as beautiful and elegant (but not perfect), and
something that EE's could really benefit from over their entire
careers. I will be using it heavily in my own programming, but not
expecting my students to learn it. It's right at the edge of what
they need to know to run the circuit design tools. That's why I'm so
interested in simplified OOP, and uninterested in floor division or
metaclasses.
Like I said, if you really are interested in making headway with your proposal,
*you* need to demonstrate through some non-contrived examples that you
understand how things work today. I've read the pages on your site and either
they don't show all cases that will be affected, or the examples are too buried
by other changes so I missed them. So, show a few examples of how bound methods
are implemented and used today in Python, and how they'd be used and
implemented the new way. Show a few examples of how unbound methods are
implemented and used today, etc. Just showing one example and stating that
everything will be like that glosses over the fact that these things are all
used in very different ways today, and leaves the reader wondering if you have
really thought things through.

I think I've done my part on this. The best examples are in
Prototypes.doc at http://ece.arizona.edu/~edatools/Python/
I'm also continually adding new examples under the Examples and
Exercises link on that same page. Many of these are contributed by
readers of this thread, including some who would rather not post here.
(Gentle Request: A little less intolerance from some others on this
thread might encourage more contributions from people they really need
to hear from.)

As for a real program, it will be a few months before I have anything
sizable. I showed you the statefile example (in response to your
request), but you seemed to be insulted, like you thought I was
talking down to you, or something.

I think the gap in our perceptions can be bridged by focusing on
examples. I have to do a lot of work to extract from general
criticism and dogmatic statements, anything that I can translate into
a specific example. Anything you can do to clarify your statements
with examples will be greatly appreciated. The 'bag' example was
perfect.
Look at the changes to Python, especially over the past few years - many of the
features are publicly credited as having come from other languages.

Yes, and that is one of the things I like about Python. GvR is not
hesitant to make changes when those changes improve the language. I'm
also impressed with the attention to beginners concerns, like the
integer division problem. GvR has a remarkable combination of
language genius and understanding of beginners' problems.

-- Dave
 
D

David MacQuigg

On the contrary, lambda has long been regarded as a wart
for various reasons. The best way to fix it is probably
to just drop it altogether. Nothing essential would be
lost.

If "lambda functions" were nothing but functions with the name left
off, they would serve their current purpose, and they would not be
such a problem that there is talk of actually taking them *out* of the
language. They aren't essential, but they do serve a purpose which
some feel is important.
We don't see static methods as clutter, because for the
most part we don't see them (i.e. we don't normally use
them). It would be no great loss if they were dropped,
too.

I would see it as a loss. The next best alternative is to move the
static method outside of the class, and that puts an arbitrary
restriction on how I can structure my programs. If the person writing
the program feels that the function belongs in the class, let's not
say it can't be done because either a) The syntax doesn't allow it. or
b) We assume he is wrong in thinking his function belongs inside his
class.

My perspective may be different, because I have not yet absorbed the
Python traditions, and I don't have any instictive avoidance of static
methods.
In neither case is it necessary to fundamentally
redesign the language to fix these, even if
they are regarded as problems.

The proposed change are minimal, and could be done in the next
non-backward compatible upgrade (Python 3?). Unlike Prothon, I am
very concerned about the ability to automatically migrate existing
Python programs to the new syntax. I believe that is possible with
the proposed syntax.

I believe the changes will make it easier for beginners to learn
Python OOP, and at worst, be a minor and temporary annoyance for
experts. I would expect learning the simpler syntax would take an
expert two hours at the most.

-- Dave
 
D

David MacQuigg

*Shakes head* OK, one change; please change the word synonyms to
anagrams in the text of the programs as well. :) When I write a
program which can algorithmically detect synonyms it'll be really
something...

Done. You can send me an email for little stuff like this that might
not interest the group.

-- Dav
 
D

David MacQuigg

If you take some wood and saw off pieces to make a box
with some fasteners, and then you put the leftover pieces
of wood *in* the box, you have pieces of wood with two different
kinds of relationship to the box.

Spoken like a Zen Master :>)
I suspect reflecting on this distinction in terms of your examples
might be fruitful.

I can't spend the time to participate more than stolen moments right now,
so I haven't seen all of the thread. Sorry if I am rehashing something.

No need for appologies. I appreciate any and all suggestions. Even
better -- a sample program that you think would be right for
introducing students to Python OOP. This can be a piece to add to the
existing box, or even an alternative box. There is room for both.

-- Dave
 
D

David MacQuigg

But I don't think you should be trying to cover all the
features of OOP before showing any real examples. That
approach seems almost guaranteed to lose people's interest,
the way you criticised supplier-part examples for.

Wouldn't it be better to do things the other way around?
Pick some (simplified if need be) examples based on
real-world problems, and, one by one, show how the
features of OOP can be used to solve them.

There are lots of ways to write a book. The way I've chosen is
following a pattern which I really liked in Introduction to Quantum
Theory by David Park. He has a brief, but thorough presentation of
all the basics, supplemented by many examples in a second section of
the book. Read a few pages of theory, then read the examples and work
the exercises. The first time through, it takes a while. Then
reviewing the course later is a pleasure.

I think it is possible to present the basics of OOP in 8 pages.
Supplementary topics like robust programming techniques might add a
few more. Examples and exercises could bring the total to 30, maybe
50 pages. We're not planning any discussion of design patterns, which
could fill a whole book. This is basic syntax only.

All of this depends on the students already understanding the topics
prior to Chapter 19, e.g. functions, modules, and global variables.

-- Dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top