[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