Using function parameters to determine method kind

  • Thread starter Lenard Lindstrom
  • Start date
L

Lenard Lindstrom

I was wondering if anyone has suggested having Python determine
a method's kind from its first parameter. 'self' is a de facto
reserved word; 'cls' is a good indicator of a class method
( __new__ is a special case ). The closest to this I could find
was the 2002-12-04 posting 'metaclasses and static methods'
by Michele Simionato. The posting's example metaclass uses the
method's name. I present my own example of automatic method
kind declaration. Being a module with some support functions
and a test case it is too large to post so is available online at:

http://www3.telus.net/len_l/Python_Explorations.html

as dparams.py .

# dparams.py example
from dparams import Object
class C(Object):
def __init__(self, x): # instance method
self.x = x
def MyClass(cls): # class method
print cls
def MyStatic(a): # static method
print a
def MyStatic2(self):
print self
MyStatic2 = staticmethod(MyStatic2) # still works


Lenard Lindstrom
<[email protected]>
 
M

Michele Simionato

Lenard Lindstrom said:
I was wondering if anyone has suggested having Python determine
a method's kind from its first parameter. 'self' is a de facto
reserved word; 'cls' is a good indicator of a class method
( __new__ is a special case ).

I find relaying on tbe parameter name to specify the kind of method
to be rather fragile. It works most times but not always. What about
inner classes for instance? Also, there are situations where you want
a method to work both with instances and classes, so do you want to
use self or cls?
I support PEP 318 also because this will kill home grown hacks (including
my own) to provide this kind of functionality.
I am also waiting for a better "super" but it seems nobody is talking
about it.

Michele Simionato
 
G

Gonçalo Rodrigues

On 5 Apr 2004 05:17:39 -0700, (e-mail address removed) (Michele
Simionato) wrote:

[text snipped]
I am also waiting for a better "super" but it seems nobody is talking
about it.

Well then, here is your chance: what are the problems of super? I also
found that it has a couple of glitches, but I'd like to know more.

With my best regards,
G. Rodrigues
 
L

Lenard Lindstrom

Michele Simionato said:
....
I find relaying on tbe parameter name to specify the kind of method
to be rather fragile. It works most times but not always. What about
inner classes for instance?
Do you mean:
from dparams import Object
class A(Object):
class B(Object):
def __init__(self, x): self.x = x
b=A.B(x)
?
Class A.B is not affected. Parameter checking only happens for
instances of type function. Other callables have to be or
use descriptors to be anything other than static. As for B's
methods, parameter checking works just fine. My module is
more of a demonstration than a practical solution anyway.
I would suggest a different approach for implementing
parameter checking in the python interpreter.
Also, there are situations where you want
a method to work both with instances and classes, so do you want to
use self or cls?
I can only imagine one way to do this now, wrap the function in a
descriptor.
.... def __init__(self, f): self.f=f
.... def __get__(self, o, t):
.... if o is None: return MethodType(self.f, t, type(t))
.... return MethodType(self.f, o, t)
.... .... def bi(x): return x
.... bi=bimethod(bi)
.... <__main__.A object at 0x0119C1B0>

Descriptors are unaffected by the function's parameters, so the first
parameter can be anything you want. If I am not mistaken, the only
methods other that __new__ that currently work without descriptors
are instance methods. Is it not good form for instance methods to
start with 'self' anyways? (except maybe for a metaclass's __init__,
but this is a special case)
I support PEP 318 also because this will kill home grown hacks (including
my own) to provide this kind of functionality.
This is a good PEP. It would complement the use of parameters.
I am also waiting for a better "super" but it seems nobody is talking
about it.
I gave it a try by changing the type argument passed to a descriptor's
__get__ method, but it required a rewritten classmethod of course.

Lenard Lindstrom
<[email protected]>
 
M

Michele Simionato

Lenard Lindstrom said:
Do you mean:
from dparams import Object
class A(Object):
class B(Object):
def __init__(self, x): self.x = x
b=A.B(x)
?

No, I had in mind this (contrived) example:

class Outer(object):
def outermeth(self1):
class Inner(object):
def innermeth(self2):
<do-something-with-self1-and-self2>
...

It is nice to have the ability to give any name to "self".
Class A.B is not affected. Parameter checking only happens for
instances of type function. Other callables have to be or
use descriptors to be anything other than static. As for B's
methods, parameter checking works just fine. My module is
more of a demonstration than a practical solution anyway.
I would suggest a different approach for implementing
parameter checking in the python interpreter.

I can only imagine one way to do this now, wrap the function in a
descriptor.

... def __init__(self, f): self.f=f
... def __get__(self, o, t):
... if o is None: return MethodType(self.f, t, type(t))
... return MethodType(self.f, o, t)
...
... def bi(x): return x
... bi=bimethod(bi)
...
<__main__.A object at 0x0119C1B0>

Descriptors are unaffected by the function's parameters, so the first
parameter can be anything you want. If I am not mistaken, the only
methods other that __new__ that currently work without descriptors
are instance methods.

?? All methods works with descriptors, what do you mean?
Is it not good form for instance methods to
start with 'self' anyways? (except maybe for a metaclass's __init__,
but this is a special case)

It is not a special case (no special case is special enough or something
like that).
 
M

Michele Simionato

Gonçalo Rodrigues said:
On 5 Apr 2004 05:17:39 -0700, (e-mail address removed) (Michele
Simionato) wrote:

[text snipped]
I am also waiting for a better "super" but it seems nobody is talking
about it.

Well then, here is your chance: what are the problems of super? I also
found that it has a couple of glitches, but I'd like to know more.

With my best regards,
G. Rodrigues

I wrote once an essay collecting all the shortcomings of "super" I found in my
explorations; maybe one day or another I will finish it and post it somewhere.
But it requires a good amount of work, so do not expect it too soon.

Michele Simionato
 
L

Lenard Lindstrom

....
No, I had in mind this (contrived) example:

class Outer(object):
def outermeth(self1):
class Inner(object):
def innermeth(self2):
<do-something-with-self1-and-self2>
...

It is nice to have the ability to give any name to "self".
Yes, I see what you mean. The best I can suggest is:

class Outer(object):
def outermethod(self1):
class Inner(object):
def innermeth(self2):
...
innermeth = instancemethod(innermeth)
outermeth = instancemethod(outermethod)

or
def outermethod(self):
self1 = self
... def innermethod(self):
self2 = self

I believe there is a use for a builtin instancemethod descriptor anyways.
It can wrap callables not of type python function.
....

?? All methods works with descriptors, what do you mean?
By descriptor I mean wrapper classes like staticmethod. A naked function
can only be an instance method descriptor, which by convention has
a first parameter of 'self'. __new__ is exceptional in that it is an
instance method that is always called as an unbound method. I see though
that __new__ would break if accidentally made a class method.
It is not a special case (no special case is special enough or something
like that).
I thought a metaclass's __init__ was a special case because it was called
from the meta meta-class, usually by method type.__call__. So maybe it
could be called as an unbound method to avoid problems if it was not
an instance method. But isn't type.__call__ the factory function for
all new-style objects, not just classes? I also overlooked cooperative
__init__ calls for multiple metaclass inheritance. So yes, there is nothing
special about a metaclass's __init__ other than it is usually declared
with 'cls' rather than 'self' as a first parameter. This is enough to
convince me that a method's first argument is not a reliable
indicator of method kind.

All this talk about python functions just being another kind
of descriptor makes me wonder if it is not time to revive
the idea of having type function subclassable:

class C(object):
def f(c):
__factory__ = classfunction # No confusion here
...

issubclass(classfunction, type(lambda: None)) # True
isinstance(C.f.im_func, classfunction) # True

:)

Thanks for answering my questions.

Lenard Lindstrom
<[email protected]>
 
M

Michele Simionato

Lenard Lindstrom said:
By descriptor I mean wrapper classes like staticmethod. A naked function
can only be an instance method descriptor, which by convention has
a first parameter of 'self'. __new__ is exceptional in that it is an
instance method that is always called as an unbound method. I see though
that __new__ would break if accidentally made a class method.

Descriptors are a protocol. If an object has a __get__ method it is a
descriptor. Python functions, methods, staticmethods, classmethods,
etc.
are all descriptors. A naked function is a descriptor, even if a
different descriptor from a bound/unbound method or a
staticmethod/classmethod; it can be converted in a staticmethod or a
classmethod or other user-defined
of descriptors. __new__ is a function which is automatically converted
to
a staticmethod and in this sense it is a special case (whereas
__init__ is
not). I think you already know all that, it was just to fix the
terminology.
But isn't type.__call__ the factory function for
all new-style objects, not just classes?


type.__call__ is tricky since "type" is its own metaclass.
What do you have in mind exactly? Classes (as opposed to poor
man instances that cannot be instantiated) are made by type.__new__
which is called by type(type).__call__ which is type.__call__.

This snippet should explain the dynamics of meta-metaclasses (which
I think you understand already, but your explanation is a bit
confusing):

class MM(type):
def __call__(mcl,name,bases,dic):
print "calling MM.__call__"
return super(MM,mcl).__call__(name,bases,dic)


class M(type):
__metaclass__=MM
def __new__(mcl,name,bases,dic):
print "calling M.__new__"
return super(M,mcl).__new__(mcl,name,bases,dic)


class C(object):
"""This class is created by the metaclass M, so M.__new__ is
called,
but not directly. First type(M).__call__ is called, i.e.
MM.__call__"""
__metaclass__=M

The output is:

calling MM.__call__
calling M.__new__
All this talk about python functions just being another kind
of descriptor makes me wonder if it is not time to revive
the idea of having type function subclassable:

You may want to look at this thread:

http://groups.google.it/groups?hl=i...ca+con+Google&meta=group%3Dcomp.lang.python.*
 
L

Lenard Lindstrom

In reply Michele Simionato's <[email protected]> posting:
<[email protected]>

I apologize that my replies are becoming more difficult to understand.
It is just that I have had to rethink a few things regarding my
initial proposal. So I will just start fresh here.

When I first posted my question on using a function's first parameter
to determine method kind I had a very specific idea on how to
implement it in Python. Type function becomes subtypable and several
builtin subtypes are made available. The root type has no __get__
descriptor method so is a static method by nature. It has two subtypes
having __get__ methods: an instancefunction is what we now call FunctionType,
and a classfunction. A function's first parameter determines which type
is created at function declaration:

def fs(x): pass # I am a static function
def fi(self): pass # I am an instance function
def fc(cls): pass # I am a class function

If the first parameter is neither 'self' or 'cls' the function
is static by default.

Of course no such scheme is worth proposing if it breaks too much
existing code. Therefore the posting to comp.lang.python. I can
imagine serious breakage only if instance methods are customarily
declared without a first argument 'self'. Instance methods are the
only case where a function's __get__ method matters. In every other
case the function is wrapped in some descriptor class instance
and the function is called directly as an unbound method.
Unfortunately there is an exception to the 'self' naming convension:
the __init__ and __new__ methods of a metaclass. I hoped they were somehow
handled differently from the usual __init__ and __new__ calls of object
instance creation, but they are not. So the 'self' coding convension
has exceptions. I am now convinced my original idea is impractical.

My example module 'dparams.py' uses a metaclass to wrap functions in
staticmethod or classmethod descriptors at class creation,
but I consider this a less than ideal solution. And to implement
it in Python means that 'object' can no longer be of type 'type'.
I believe this is unacceptable. So I consider the issue of using
parameters to determine method kind closed.

I was actually being somewhat serious. It is just the syntax of:

def foo(x):
__factory__ = staticfunction

that is questionable.

def(staticfunction) foo(x):
...

may be more appropriate.

As for that thread you mentioned in you posting on subtyping
FunctionType, here it is:

http://groups.google.com/groups?hl=...8519782.21597.python-list%40python.org&rnum=1

I actually did make FunctionType subtypable in a prerelease
version of python 2.3 and it did not crash or act strange.
At the time I made the above posting I was playing with function
currying and tried to extend FunctionType by adding some curry
operators. It didn't work:

"TypeError: type 'function' is not an acceptable base type"

But as the thread continued my ideas on function currying changed
and it seemed less important to subtype function, so my postings stop
making a lot of sense; it became more important to me to have
a consistent way of doing introspection on callables. I made
FunctionType subtypable just to prove a point. I was a
stranger to the Python community and the Unix diff and patch
tools so I did not try to submit a patch. The interpreter has
since dissappeared into my trashbin as newer releases of Python
can out. But if there is enough interest and I have the time I
may try and do it again.

Lenard Lindstrom
<[email protected]>
 
H

Humpty Dumpty

From: "Michele Simionato said:
No, I had in mind this (contrived) example:

class Outer(object):
def outermeth(self1):
class Inner(object):
def innermeth(self2):
<do-something-with-self1-and-self2>
...

It is nice to have the ability to give any name to "self".
like that).

Strange, I would have thought that self1 would not be in scope (it's not in
the local scope and it's not in the global scope AFAICT). If true, you'd
have to have a parameter to your innermeth, which you can call whatever you
want so call it self1. Have scoping rules changed in Python?

Oliver
 

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

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top