More Python voodoo: combination class/instance methods
Posted by David Zaslavsky on — CommentsId: 86
Here’s a neat tidbit from my website code: Let’s say you’re writing a class, and you want it to have some methods that can be called as either class or instance methods. Of course, you can do it by writing a class method:
class F(object):
@classmethod
def foo(cls, a, b, c):
print a, b, c
F.foo(1, 2, 3)
F().foo(1, 2, 3)
But what if you want a pattern like the following instead, where the method, when called on an instance, can use the instance’s attributes?
F.foo(1, 2, 3)
F(1, 2, 3).foo()
You can do this with a descriptor. Descriptors allow you to customize the way attributes are accessed (and set); you can define precisely what you want F.foo
or F().foo
to do. This is kind of like the __getattribute__
method, but more general because you can use descriptors for class attributes as well.
class FooDescriptor(object):
def __init__(self, method_name):
super(FooDescriptor, self).__init__()
self.method_name = method_name
def __get__(self, instance, owner):
if instance is None:
# As a class attribute
def foo(cls, a, b, c):
print a, b, c
else:
# As an instance attribute
def foo(self):
print self.a, self.b, self.c
return foo
The downside of this procedure is that you have to write two different definitions of the method, although that’s generally only a minor inconvenience. If you’re using this technique for a long method, it’s not hard to split out most of the code into a common function that gets called from both the class method and instance method versions.