I want to restrict methods to be used on instance objects, not on class. Does this break aspects of the data model, i.e. calls to super()? Normally, the following works:
class Foo:
def bar(self, x=None):
print(x)
Foo().bar(42)
Foo.bar(Foo(), 42)
I want to prevent the second case, because for some reason people make the mistake of calling Foo.bar(42), which causes unanticipated errors that change as bar gets refactored (here it prints None). Following the tutorial on data descriptors I use the descriptor-decorator:
from types import MethodType
class OnlyMethodNotUnboundFunction:
def __init__(self, func):
self.func = func
def __get__(self, obj, objtype=None):
if obj is None:
# Changed from `return self`
raise UnboundLocalError("This method can only be used by an instantiated object, not a Class")
return MethodType(self.func, obj)
class Foo():
@OnlyMethodNotUnboundFunction
def bar(self, x=None):
print(x)
Now, Foo().bar(42) works as anticipated, but Foo.bar(42) raises the UnboundLocalError, as desired. However, if I subclass Foo and call super, the behavior of the descriptor is not inherited in either call to super():
class Baz1(Foo):
def bar(self, x=None):
super().bar(x)
print(x + 1)
class Baz2(Foo):
def bar(self, x=None):
super(Baz2, self).bar(x)
print(x + 1)
Both Baz1.bar(Baz1(), 42) and Baz2.bar(Baz2(), 42) print 42 and 43. I had thought that the call to super() in Baz1 would return the class Foo, not an instance. The MRO would be the same in either case, but Baz1.bar(Baz1(), 42) should cause a call to Foo.bar(42) and raise the UnboundLocalError.
What's going on here? Does super() detect the presence of a passed self argument and automatically return an instance? Moreover, is there a way to inherit descriptors from parent class so that Baz2.bar(Baz2(), 42) also raises an error?