[Python-Dev] Re: Guido's Magic Code was: inline sort option

Alex Martelli aleaxit at yahoo.com
Thu Oct 30 08:54:48 EST 2003


On Thursday 30 October 2003 02:01 pm, Michael Chermside wrote:
> Raymond writes:
> > If only in the C API, I would like to see just such a universalmethod
> > alternative to classmethod.  That would allow different behaviors to be
> > assigned depending on how the method is called.
>
> And that's exactly why I would be wary of it. One of the GREAT SIMPLICITIES
> of Python is that all calls are "the same". Calling a function works

A new descriptortype wouldn't change the ``all the same'' idea at
the level at which descriptortypes such as staticmethod and classmethod
haven't changed it.

> a particular way. Calling a callable object does the same, although
> the arguments are passed to __call__. Calling a classmethod does the
> same thing. Calling a bound method does the same thing except that the
> first argument is curried. Here in the Python community, we think it
> is a good thing that one must explicitly name "self" as an argument to
> methods, and that any function CAN be made a method of objects.

Nothing of this would change.  Just consider calling a method on the
class or on an instance for various descriptortypes:
    for staticmethod:
        aclass.foo()      # ignores the exact classobject
        aninst.foo()      # ignores the instanceobject & its exact class
    for classmethod:
        aclass.bar()      # passes the exact classobject
        aninst.bar()      # ignores the instanceobject, passes its __class__
    for functions (which are also descriptors):
        aclass.baz(aninst)    # must explicitly pass an instance of aclass
        aninst.baz()             # pases the instance

so, we do have plenty of variety today, right?  Consider the last couple
in particular (it's after all the most common one): you have the specific
constraint that aninst MUST be an instance of aclass.

So what we're proposing is JUST a descriptortype that will relax the
latter constraint: aninst.wee() passes the instance (just like the latter
couple), aclass.wee(beep) does NOT constrain beep to be an instance
of a class but is more relaxed, allowing the code of 'wee' to determine
what it needs, what it has received, etc -- just like in about ALL cases
of Python calls *except* "aclass.baz(aninst)" which is an exceptional
case in which Python itself does (enforced) typechecking for you.  So
what's so bad about optionally being able to do w/o that typecehecking?

I've mentioned use cases already -- besides list.sorted -- such as
gmpy's sqrt and fsqrt which would more naturally be modeled as
just such methods, rather than instancemethods (named sqrt) of
types mpz and mpf resp., also available with different names as
module-functions (to bypass the typechecking and do typecasting
instead).  More generally, the idea is that aclas.wee(beep) is just
about equivalent to aclas(beep).wee() but may sometimes be
implemented more optimally (avoiding the avoidable construction
of a temporary instance of aclas from beep, when that is a costly
part), and it's better sided in class aclas than as some module
function "aclas_wee" (stropping by the typename or some other
trick to avoid naming conflict if 'wee' methods of several types
are forced into a single namespace because Python doesn't let
them be attributes of their respective types in these cases).

I don't see any revolution in Python's calls system -- just a little
extra possibility that will sometimes allow a better and more natural
(or better optimizable) placement of functionality that's now not
quite comfortably located as either instancemethod, classmethod
or module-level function.


> Now you're proposing a special situation, where what appears to be
> a single attribute of a class object is actually TWO functions...
> two functions that have the same name but subtly different behavior.

Nah -- not any more than e.g. a property "is actually THREE functions".
A property may HOLD three functions and call the appropriate one in
appropriate cases, x.y=23 vs print x.y vs del x.y.  In general, a descriptor
"has" one or more callables it holds (a function "has" AND "is").

> Right now, I presume that if I have:
>       a = A()   # a is an instance of A
>       x = a.aMethod('abc')   # this is line 1
>       y = A.aMethod(a, 'abc')    # this is line 2
> that line 1 and line 2 do the same thing. This is a direct consequence
> of the fact that methods in Python are just functions with an instance
> as the first argument. But your "universalmethod" would break this.

Actually, unless you show me the source of A, I cannot be absolutely
sure that your presumption is right, even today.  A simple example:

class A(object):
    def aMethod(*allofthem): return allofthem
    aMethod = staticmethod(aMethod)

Now, the behavior of lines 1 and 2 is actually quite different -- x is
a singleton tuple with a string, y a pair whose first item is an instance
of A and the second item a string.

Sure, your presumption is reasonable and a reasonable programmer
will try to make sure it's valid, but Python already gives the programmer
plenty of tools with which to make your presumption invalid.

The _design intention_ of universalmethod would be to still satisfy
your presumption, PLUS allow calls to A.aMethod(bbb, 'abc') for any
"acceptable" object bbb, not necessarily an instance of A, to do
something like A(bbb).aMethod('abc') although possibly in a more
optimized way (not necessarily constructing a temporary instance
of A, if that is costly and can be easily avoided).  Of course it can
ALSO be used unreasonably, but so can lots of existing descriptors, too.


> It might be worth breaking it, if the result is some *very* readable

Can't break what's already broken:-).

> to make it a regular and supported idiom, I'd want to see much better
> evidence that it's worthwhile, because there's an important principle
> at risk here, and I wouldn't want to trade away the ability to explain
> "methods" in two sentences:
>     A 'method' is just a function whose first argument is 'self'.
>     The method is an atribute of the class object, and when it is
>     called using "a.method(args)", the instance 'a' is passed as
>     'self'.
> for a cute way of making double use of a few factory functions.

I don't think of it as "cute", but rather more appropriate than currently
available solutions in some such cases (already exemplified).

 And those sentences are already false if by 'method' you also want 
to include staticmethod and classmethod.  If you intend 'method' in
a stricter sense that excludes staticmethod and classmethod, why,
just have your stricter sense ALSO exclude universalmethod and,
voila, you can STILL "explain methods in two sentences".

Thus, there is no "important principle at risk" whatsoever.


Alex
 



More information about the Python-Dev mailing list