I have some code which I would like to pass instances or classes interchangeably. All I will do in that code is to call a method that I expect both classes and instances to have (the method go()
in the example below).
Unfortunately, I can't create a classmethod with the same name of a regular method... See example below. I initially expected the second call to produce an a
instead of a b
.
Any advice on how to achieve this?
Type "help", "copyright", "credits" or "license" for more information.
>>> class A(object):
... def go(self):
... print "a"
... @classmethod
... def go(cls):
... print "b"
...
>>> a=A()
>>> a.go()
b
>>> A.go()
b
Consider reusing the classinstancemethod
decorator from formencode.
https://bitbucket.org/formencode/official-formencode/src/06d52c5b33c9/formencode/declarative.py
class classinstancemethod(object):
"""
Acts like a class method when called from a class, like an
instance method when called by an instance. The method should
take two arguments, 'self' and 'cls'; one of these will be None
depending on how the method was called.
"""
def __init__(self, func):
self.func = func
def __get__(self, obj, type=None):
return _methodwrapper(self.func, obj=obj, type=type)
class _methodwrapper(object):
def __init__(self, func, obj, type):
self.func = func
self.obj = obj
self.type = type
def __call__(self, *args, **kw):
assert 'self' not in kw and 'cls' not in kw, (
"You cannot use 'self' or 'cls' arguments to a "
"classinstancemethod")
return self.func(*((self.obj, self.type) + args), **kw)
def __repr__(self):
if self.obj is None:
return ('<bound class method %s.%s>'
% (self.type.__name__, self.func.func_name))
else:
return ('<bound method %s.%s of %r>'
% (self.type.__name__, self.func.func_name, self.obj))
You could create an own method type with a specially crafted __get__()
method.
In this method, you could do something like this:
class combimethod(object):
def __init__(self, func):
self._func = func
def classmethod(self, func):
self._classfunc = classmethod(func)
return self
def __get__(self, instance, owner):
if instance is None:
return self._classfunc.__get__(instance, owner)
else:
return self._func.__get__(instance, owner)
class A(object):
@combimethod
def go(self):
print "instance", self
@go.classmethod
def go(cls):
print "class", cls
a=A()
print "i:",
a.go()
print "c:",
A.go()
NOTE: The above is not very thoroughly tested, but seems to work. Nevertheless, it should be seen as a kind of "solution-near pseudo-code", not as a solution. It should give you an idea how to achieve your goal.
How about something like:
import inspect
class A(object):
@staticmethod
def go(obj):
if inspect.isclass(obj):
print 'class'
else:
print 'instance'
A.go(int) # class
A.go(1) # instance
A.go(A) # class
A.go(A()) # instance