P
Paul Morrow
One of the beautiful things about Python is its clear, minimal syntax.
So we must resist adding new syntax to the language, especially where
there is a reasonable alternative.
I believe that Stefen Eischet's suggestion for automatically determining
a method's type (class/instance/static) from the name of its first
formal parameter is a reasonable alternative to any/all of the decorator
syntax proposals.
Just as the Python system relies on indentation conventions to denote
the programmer's intended block structure, it could just as well rely on
parameter naming conventions to denote method types. And as long as
these conventions feel natural to the programmer, the language retains
its beauty.
So here is a metaclass that illustrates (what I believe is) the
essential idea behind Stefen's suggestion. See the docstring and
footnotes for a description of what it does.
##################################################################
class AntiDecorator(type):
""" Metaclass that protests against decorator syntax
This metaclass infers a method's method type (instance, class,
or static) from the name of its first formal parameter, then
makes the necessary Python declaration.
* If the 1st parm's name is 'self', then the method is
an instance method.
* If the 1st parm's name is 'cls' or 'klass', then the
method is a class method.
* All other methods are static methods.
-------------------------------------------------
The essence of this technique was suggested by
Stefan Eischet on the comp.lang.python newsgroup.
-------------------------------------------------
This is freeware, no warranties, etc...
"""
__author__ = 'Paul Morrow <[email protected]>'
__credits__ = 'Stefen Eischet <[email protected]>'
__date__ = '13 Aug 04'
__version__ = '0.1'
def __new__(cls, clsName, bases, dict):
import inspect, types
for fName in dict.keys():
f = dict[fName] # 1.
if type(f) is types.FunctionType: # 2.
parmNames = inspect.getargspec(f)[0] # 3.
if parmNames: # 4.
if parmNames[0] in ('cls', 'klass'): # 5.
dict[fName] = classmethod(f)
elif parmNames[0] == 'self': # 6.
pass
else: # 7.
dict[fName] = staticmethod(f)
else: # 8.
dict[fName] = staticmethod(f)
return type.__new__(cls, clsName, bases, dict)
"""Footnotes:
1. Bind f to an attribute of the class (cls).
2. Only work this magic on functions.
3. Get the function's formal parameter names.
4. If it has formal parameters, we'll use them
to determine what kind of function it is.
5. It's a class method if its first formal
parameter is 'cls' or 'klass'.
6. It's an instance method (default) if its first
formal parm is 'self'
7. It's a static method if it's not a class method
or instance method.
8. No formal parameters, so it's a static method.
"""
class Object(object):
""" Uses AntiDecorator metaclass to infer method type. """
__metaclass__ = AntiDecorator
if __name__ == '__main__':
class Foo(Object):
def imethod(self, parm):
print "I'm an instance method: %s." % parm
def cmethod(cls, parm):
n = cls.__name__
print "I'm a class method (of %s): %s." % (n, parm)
def smethod(parm):
print "I'm a static method: %s." % parm
Foo().imethod('alpha')
Foo.smethod('beta')
Foo.cmethod('gamma')
##################################################################
So we must resist adding new syntax to the language, especially where
there is a reasonable alternative.
I believe that Stefen Eischet's suggestion for automatically determining
a method's type (class/instance/static) from the name of its first
formal parameter is a reasonable alternative to any/all of the decorator
syntax proposals.
Just as the Python system relies on indentation conventions to denote
the programmer's intended block structure, it could just as well rely on
parameter naming conventions to denote method types. And as long as
these conventions feel natural to the programmer, the language retains
its beauty.
So here is a metaclass that illustrates (what I believe is) the
essential idea behind Stefen's suggestion. See the docstring and
footnotes for a description of what it does.
##################################################################
class AntiDecorator(type):
""" Metaclass that protests against decorator syntax
This metaclass infers a method's method type (instance, class,
or static) from the name of its first formal parameter, then
makes the necessary Python declaration.
* If the 1st parm's name is 'self', then the method is
an instance method.
* If the 1st parm's name is 'cls' or 'klass', then the
method is a class method.
* All other methods are static methods.
-------------------------------------------------
The essence of this technique was suggested by
Stefan Eischet on the comp.lang.python newsgroup.
-------------------------------------------------
This is freeware, no warranties, etc...
"""
__author__ = 'Paul Morrow <[email protected]>'
__credits__ = 'Stefen Eischet <[email protected]>'
__date__ = '13 Aug 04'
__version__ = '0.1'
def __new__(cls, clsName, bases, dict):
import inspect, types
for fName in dict.keys():
f = dict[fName] # 1.
if type(f) is types.FunctionType: # 2.
parmNames = inspect.getargspec(f)[0] # 3.
if parmNames: # 4.
if parmNames[0] in ('cls', 'klass'): # 5.
dict[fName] = classmethod(f)
elif parmNames[0] == 'self': # 6.
pass
else: # 7.
dict[fName] = staticmethod(f)
else: # 8.
dict[fName] = staticmethod(f)
return type.__new__(cls, clsName, bases, dict)
"""Footnotes:
1. Bind f to an attribute of the class (cls).
2. Only work this magic on functions.
3. Get the function's formal parameter names.
4. If it has formal parameters, we'll use them
to determine what kind of function it is.
5. It's a class method if its first formal
parameter is 'cls' or 'klass'.
6. It's an instance method (default) if its first
formal parm is 'self'
7. It's a static method if it's not a class method
or instance method.
8. No formal parameters, so it's a static method.
"""
class Object(object):
""" Uses AntiDecorator metaclass to infer method type. """
__metaclass__ = AntiDecorator
if __name__ == '__main__':
class Foo(Object):
def imethod(self, parm):
print "I'm an instance method: %s." % parm
def cmethod(cls, parm):
n = cls.__name__
print "I'm a class method (of %s): %s." % (n, parm)
def smethod(parm):
print "I'm a static method: %s." % parm
Foo().imethod('alpha')
Foo.smethod('beta')
Foo.cmethod('gamma')
##################################################################