[Python-ideas] class-only methods without using metaclasses

Eric Snow ericsnowcurrently at gmail.com
Thu Mar 7 08:10:20 CET 2013


This tweet from Raymond helped distill something that was already on
my mind of late:

  https://twitter.com/raymondh/status/309442149588533248

For some uses of @classmethod it makes sense to expose the methods on
instances too.  On others I just can't see it.  In those cases, using
@classmethod is justifiably a practical substitute for putting the
methods on a metaclass.  In my mind "alternate constructors" fall into
this category.

Would it be worth trying to get the best of both worlds (not exposed
on instances but without metaclasses)?  I can imagine providing a
classmethod-like decorator that does this and have an implementation
below.

One benefit to not exposing the class-only methods on instances is
that they don't clutter the instance namespace nor run the risk of
colliding with instance-specific names.

Thoughts?

-eric

-----------------------------------------------------------------------

class classonlymethod:
    """Like a classmethod but does not show up on instances.

    This is an alternative to putting the methods on a metaclass.  It
    is especially meaningful for alternate constructors.

    """
    # XXX or "metamethod"

    def __init__(self, method):
        self.method = method
        self.descr = classmethod(method)

    def __get__(self, obj, cls):
        name = self.method.__name__
        getattr_static = inspect.getattr_static

        if obj is not None:
            # look up the attribute, but skip cls
            dummy = type(cls.__name__, cls.__bases__, {})
            attr = getattr_static(dummy(), name, NOTSET)
            getter = getattr_static(attr, '__get__', None)

            # try data descriptors
            if (getter and getattr_static(attr, '__set__', False)):
                return getter(attr, obj, cls)

            # try the instance
            try:
                instance_dict = object.__getattribute__(obj, "__dict__")
            except AttributeError:
                pass
            else:
                try:
                    return dict.__getitem__(instance_dict, name)
                except KeyError:
                    pass

            # try non-data descriptors
            if getter is not None:
                return getter(attr, obj, cls)

            raise AttributeError(name)
        else:
            descr = vars(self)['descr']
            return descr.__get__(obj, cls)



More information about the Python-ideas mailing list