One possible approach without modifying Foo is to define bar as an instance method of a subclass, but use a custom descriptor to make attribute access to the method return the class method of the same name of the super class instead when the attribute is accessed from the class, when the instance passed to __get__ is None:
class hybridmethod:
def __init__(self, f):
self.f = f
def __get__(self, obj, cls=None):
if obj is not None:
return self.f.__get__(obj)
return getattr(super(cls, cls), self.f.__name__).__get__(cls)
class MyFoo(Foo):
@hybridmethod
def bar(self):
return self.text
print(MyFoo.bar()) # outputs None
foo = MyFoo('foo')
print(foo.bar()) # outputs foo
Demo: https://ideone.com/yVAzr3
EDIT: Since you have now clarified in the comment that you have full control over Foo, you can instead modify Foo.bar directly such that it is a custom descriptor whose __get__ method calls a class method or an instance method based on the aforementioned condition.
For convenience I've made the descriptor a decorator that decorates a class method with an additional instancemethod method that can decorate an instance method:
class instanceable_classmethod(classmethod):
def __init__(self, func):
super().__init__(func)
self.instance_func = self.__func__
def instancemethod(self, func):
self.instance_func = func
return self
def __get__(self, obj, cls=None):
if obj is None:
return super().__get__(obj, cls)
return self.instance_func.__get__(obj)
class Foo(object):
def __init__(self, text):
self.text = text
@instanceable_classmethod
def bar(cls):
return None
@bar.instancemethod
def bar(self):
return self.text
print(Foo.bar()) # outputs None
foo = Foo('foo')
print(foo.bar()) # outputs foo
Demo: https://ideone.com/PmSnUC