classmethod() not inherited?

Jacob Smullyan smulloni at smullyan.org
Fri Jan 17 22:12:00 EST 2003


In article <mailman.1042712132.3419.python-list at python.org>, Andrew Bennetts wrote:
> On Thu, Jan 16, 2003 at 04:43:35AM -0500, Jack Diederich wrote:
>> I'd like to have ALL of the derivitives of Base
>> have foo as a classmethod, but it doesn't seem
>> to be automatic and the following doesn't work
> Well, foo *is* a classmethod in the derived classes -- it's just that you're
> replacing it with a foo that's not a classmethod <wink>.
> 
> If you really want this, you could write a metaclass:
> 
> ---
> class M(type):
>     def __init__(cls, name, bases, dict):
>         type.__init__(cls, name, bases, dict)
>         if callable(dict.get('foo')):
>             setattr(cls, 'foo', classmethod(dict['foo']))
> 
> class Base(object):
>     __metaclass__ = M      # removing this line gives the old behaviour
>     def foo(cls):
>       return 'BASE!', cls
> 
> # ... A, B and Z as before ...
> 
> for x in (Base(), A(), B(), Z()):
>     print x.foo()
> ---
> 
> This will print:
> ('BASE!', <class '__main__.Base'>)
> ('some value', <class '__main__.A'>)
> ('some other value', <class '__main__.B'>)
> ('something else still', <class '__main__.Z'>)
> 
> Instead of:
> ('BASE!', <__main__.Base object at 0x8121a6c>)
> ('some value', <__main__.A object at 0x81141d4>)
> ('some other value', <__main__.B object at 0x811345c>)
> ('something else still', <__main__.Z object at 0x814c644>)
> 
> Of course, extending it to do this to all overridden classmethods, instead
> of being hard-coded to 'foo', is a little trickier, but not much...
> 
 
For instance, the below uses a naming convention so the metaclass can
to determine which methods are class methods, and then classmethod-ify
them.  Static methods are thrown in for good measure:

class _classymeta(type):
    def __new__(self, cl_name, bases, namespace):
	prefix_descriptor_pairs=(('static_', staticmethod),
                                 ('class_', classmethod))
        for k, v in namespace.items():
            for prefix, descriptor in prefix_descriptor_pairs:
                lenpre=len(prefix)
                if k.startswith(prefix):
                    methname=k[lenpre:]
                    if methname in namespace:
                        raise ValueError, \
                            "duplicate member name: %s" % methname
                    namespace[methname]=descriptor(v)
                    del namespace[k]
        return type.__new__(self, cl_name, bases, namespace)


class thing(object):
    __metaclass__=_classymeta

    def static_foo(x, y):
        print x, y

    def class_foo2(k, x, y):
        print k, x, y

class derivedthing(thing):

    def class_foo2(k, x, y):
        thing.foo2(x, y)
        print "in foo2"

t=thing()
t.foo(1, 2)
t.foo2(1, 2)
v=derivedthing()
v.foo2(1, 2)




More information about the Python-list mailing list