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

Raymond writes:
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 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. 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. 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. It might be worth breaking it, if the result is some *very* readable code in a whole variety of situations. And it's certainly okay for Guido to manually create a class which behaves this way via black magic (and for most users, descriptor tricks are black magic). But 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. -- Michael Chermside

On Thursday 30 October 2003 02:01 pm, Michael Chermside wrote:
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.
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.
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").
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:-).
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

On Thursday 30 October 2003 08:54 am, Alex Martelli wrote:
Out of curiosity, why does Python do this typechecking? I just ran into a situation where such calls in my subclass of sets.Set fail if the sets module gets reloaded. Is there some really important reason why in this case (and only this case) Python does typechecking on pure-Python classes? Jeremy

On Thursday 30 October 2003 02:01 pm, Michael Chermside wrote:
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.
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.
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").
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:-).
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

On Thursday 30 October 2003 08:54 am, Alex Martelli wrote:
Out of curiosity, why does Python do this typechecking? I just ran into a situation where such calls in my subclass of sets.Set fail if the sets module gets reloaded. Is there some really important reason why in this case (and only this case) Python does typechecking on pure-Python classes? Jeremy
participants (3)
-
Alex Martelli
-
Jeremy Fincher
-
Michael Chermside