0

I have an abstract class ship.

from abc import ABC, abstractmethod
class ship(ABC):
    def __init__(self):
         ...

    @abstractmethod
    def do_stuff(self,stuff,things):
         pass

I have multiple classes that inherit from it (destroyer,cruiser,patrol_boat, etc...)

class carrier(ship):
    def __init__(self):
         ....
    def do_stuff(self,stuff,things):
         ....

Currently, if I were to add, let's say def do_more_stuff(self): to ship

class ship(ABC):
    def __init__(self):
         ...

    @abstractmethod
    def do_stuff(self,stuff,things):
         pass

    @abstractmethod
    def do_more_stuff(self,stuff,things):
        pass

The changes would not affect any of the subclasses until I reentered them into the console. How do I change this?

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
David
  • 89
  • 9
  • You need to give enough code to show what you actually did ([MVCE](https://stackoverflow.com/help/MCVE)). Editing the super _class_ to add new methods should work (it's mildly inefficient to do so after the subclasses are defined, but not unreasonably so), but I suspect you're mistakenly editing _instances_, or not editing the class at all. – ShadowRanger Dec 09 '16 at 02:03
  • I feel like I'm redefining `ship` while leaving the remaining classes to inherit from the previous definition of `ship`. How could I add functions to `ship` without reentering the entire block? – David Dec 09 '16 at 02:12
  • You're correct about redefining the whole class not changing what the subclasses inherit from. My answer covers the details here; there is a way that works for non-abstract methods, but abstract methods cannot use this technique. – ShadowRanger Dec 09 '16 at 02:19

1 Answers1

1

If you actually redefine a class from scratch, it's a different class; the subclasses are still inheriting from the old version of ship. You can't just define a new class named ship and expect the subclasses to find it magically.

Normally, if you wanted to monkey-patch ship after creation to add new methods, you could just do something like:

def do_more_stuff(self,stuff,things):
    pass
ship.do_more_stuff = do_more_stuff

But unfortunately, abstractmethods for ABCs are an explicit exception to this rule:

Dynamically adding abstract methods to a class, or attempting to modify the abstraction status of a method or class once it is created, are not supported.

You must either define the abstract base class completely up front, or redefine the entire class hierarchy later if you want to add new abstract methods to the base class.

ShadowRanger
  • 143,180
  • 12
  • 188
  • 271
  • Could I add regular methods to the base abstract class without encountering this problem? – David Dec 09 '16 at 02:33
  • @David: That should not cause problems. The magic is involved with the combination of the `ABCMeta` metaclass and the `abstractmethod` descriptor magic; the metaclass hooks are invoked at the moment you end the class definition block (thus the requirement to define all abstract methods with the class), and `ABCMeta` is specifically concerned with `abstractmethod`, not regular methods. As long as you don't try to replace existing abstract methods or add new ones, it should work just fine; new concrete methods can't change the rules for abstract inheritance. – ShadowRanger Dec 09 '16 at 02:52