Code snippet: dualmethod descriptor
steve at REMOVE-THIS-cybersource.com.au
Sat Jan 30 07:06:18 CET 2010
I came up with this descriptor a few years ago, but never used it for
anything. I've just found an actual use for it in my own code, so I
thought I'd make it public for anyone else who might have a use for it.
Use-case: if you have a class without an __init__ or __new__ method, then
this may be of use to you. It enables the caller to call methods on
either the class, or an instance, and the first argument passed the
method will be the class or the instance respectively.
This differs from classmethods, which always passes the class, and
staticmethods, which don't pass either.
The name "dualmethod" is my own.
Here's the implementation:
"""Descriptor implementing dualmethods (combination
Returns a method which takes either an instance or a class as
the first argument. When called on an instance, the instance is
passed as the first argument. When called as a class, the class
itself is passed instead.
>>> class Example(object):
... def method(this):
... if type(this) is type:
... print("I am the class '%s'." % this.__name__)
... print("I am an instance of the class '%s'." %
I am the class 'Example'.
I am an instance of the class 'Example'.
def __init__(self, func):
self.func = func
def __get__(self, obj, cls=None):
if obj is None: obj = cls or type(obj)
def newfunc(*args, **kwargs):
return self.func(obj, *args, **kwargs)
(Tested with Python 2.4, 2.5, 2.6, 3.0 and 3.1.)
My use-case is a class that has constants in class attributes, and
methods which refer to those constants. Because they are constants, there
is no reason to give each instance a separate reference to the attribute,
hence the attribute is on the class:
ATTR = "something"
return this.ATTR + " else"
Normally I have no reason to instantiate the class: there is no __init__
or __new__ method. Normally I would use classmethod, but every now and
again I want to change the behaviour by modifying ATTR, and using class-
methods would force me to subclass in order to make that change.
dualmethod lets me get the best of both worlds. If I'm using the default
value of ATTR, I can just call the method on the class without bothering
to instantiate it. But if I want to customise the behaviour of the
method, rather than subclassing, I can instantiate and add an instance
>>> x = Example()
>>> x.ATTR = "nothing"
>>> Example.method() # default still works as expected
without effecting the default behaviour.
I hope this is useful to you. You can use this without condition,
although I would be grateful for credit.
More information about the Python-list