3

Suppose I have two classes, one inheriting from the other :

class A():
    def __init__(self):
        pass

    def doSomething(self):
        print('It Works !') # Insert actual code here

class B(A):
    pass

How do I make the doSomething method impossible to inherit, so that : ( I want to make the error happen )

>>> a = A()
>>> a.doSomething()
'It Works !'
>>> b = B()
>>> b.doSomething()
Traceback (most recent call last):
  File "<pyshell#132>", line 1, in <module>
    b.doSomething()
AttributeError: 'B' object has no attribute 'doSomething'
ice-wind
  • 690
  • 4
  • 20

2 Answers2

2

You should question whether you want to have a non-inheritable part in the first place. It would be more typical to abstract out the common parts of A and B into a common parent, or use a mixin pattern.

    class Parent
     def doCommonThing1
     def doCommonThing2
     /          \
    /            \
   /              \
  /                \
class A            class B
 def doSomething    def doOtherThing

If you insist that B must be a subclass of A, then the only way to "uninherit" a method is to override it to do something else. For example, a property which raises attribute error is for all practical purposes the same as a missing attribute:

>>> class A:
...     def doSomething(self):
...         print("it works")
... 
>>> class B(A):
...     @property
...     def doSomething(self):
...         msg = "{!r} object has no attribute 'doSomething'"
...         raise AttributeError(msg.format(type(self).__name__))
... 
>>> A().doSomething()
it works
>>> hasattr(B(), "doSomething")
False
>>> B().doSomething()
...
AttributeError: 'B' object has no attribute 'doSomething'
wim
  • 338,267
  • 99
  • 616
  • 750
1

To the best of my knowledge, there is no builtin way to do this in Python, because it is not really considered part of the Python philosophy. There can define "protected" and "private" methods in Python by prepending a single _ or double __, but you can still call those, it's just discouraged.

One very hacky way to achieve something similar might be to make the method itself "private" and have __getattr__ redirect to that method, but only if the object is really an A.

class A():
    def __init__(self):
        pass

    def __doSomething(self):
        print('It Works !')
        
    def __getattr__(self, attr):
        if attr == "doSomething":
            if type(self) == A:
                return self.__doSomething
            else:
                raise TypeError("Nope")
        return super(A).__getattr__(self, attr)

But this could still be circumvented by calling the "private" method directly as _A__doSomething or overwriting __getattr__ in B.

Alternatively, possibly safer and probably simpler (but still pretty hacky IMHO), you could also add that check to doSomething itself.

    def doSomething(self):
        if type(self) != A:
            raise TypeError("Nope")
        print('It Works !')
tobias_k
  • 81,265
  • 12
  • 120
  • 179