Yes, using the magic of descriptors. See my blog post about it. Short version:
class nosubclasses(object):
def __init__(self, f, cls):
self.f = f
self.cls = cls
def __get__(self, obj, type=None):
if type == self.cls:
if hasattr(self.f, '__get__'):
return self.f.__get__(obj, type)
return self.f
raise AttributeError
Example:
In [2]: class MyClass(object):
...: x = 1
...:
In [3]: MyClass.x = nosubclasses(MyClass.x, MyClass)
In [4]: class MySubclass(MyClass):
...: pass
...:
In [5]: MyClass.x
Out[5]: 1
In [6]: MyClass().x
Out[6]: 1
In [80]: MySubclass.x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-80-2b2f456dd101> in <module>()
----> 1 MySubclass.x
<ipython-input-51-7fe1b5063367> in __get__(self, obj, type)
8 return self.f.__get__(obj, type)
9 return self.f
---> 10 raise AttributeError
AttributeError:
In [81]: MySubclass().x
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-81-93764eeb9948> in <module>()
----> 1 MySubclass().x
<ipython-input-51-7fe1b5063367> in __get__(self, obj, type)
8 return self.f.__get__(obj, type)
9 return self.f
---> 10 raise AttributeError
AttributeError:
But as the commenter @delnan pointed out, this violates the Liskov substitutability principle. The motivation in my blog post was warranted, because the attribute did not describe the object itself. But in general, this breaks the whole point of being able to subclass in the first place, which is really the whole point of having classes at all.
By the way, the difference between my answer and @jamylak's is that in @jamylak's answer, attributes are removed on a per-subclass basis. If you made a class C(A)
, it would still have the bar
attribute. In my answer, the class itself (well, actually the attribute), disallows subclasses from having the attribute, so that in one fell swoop, all subclasses don't have it.