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?
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.
Related
For classes:
class Base(ABC):
def __init__(self, param1):
self.param1 = param1
#abstractmethod
def some_method1(self):
pass
# #abstractmethod
# def potentially_shared_method(self):
# ????
class Child(Base):
def __init__(self, param2):
self.param1 = param1
self.param2 = param2
def some_method1(self):
self.object1 = some_lib.generate_object1(param1, param2)
def potentially_shared_method(self):
return object1.process()
I want to move the potentially_shared_method to be shared in abstract calss, however it uses object1 that is initialized in some_method1 and needs to stay there.
If it's only potentially shared, it doesn't belong in the base class. You'd be breaking a few design principles.
What is a child class supposed to do for which the sharing doesn't make sense?
Also, you're introducing some temporal coupling; you can only call potentially_shared_method after some_method1 has been called. That's not ideal because the users of your class might not realize that.
Also, if the method is shared, you probably don't want it to be abstract in your base class; with an abstract method you're really only sharing the signature; but it seems you'll want to share functionality.
Anyway. Here's some options:
Using Python's multiple inheritance, move potentially_shared_method into a SharedMixin class and have those children who share it inherit from Base and from SharedMixin. You can then also move some_method1 into that SharedMixin class because it seems to me that those go together. Or maybe not...
Hide the access to object1 behind a getter. Make the getter have a dummy implementation in the base class and a proper implementation in those child classes who actually create an object1. Then potentially_shared_method can be moved to Base and just refer to the getter.
I am given a designated factory of A-type objects. I would like to make a new version of A-type objects that also have the methods in a Mixin class. For reasons that are too long to explain here, I can't use class A(Mixin), I have to use the A_factory. Below I try to give a bare bones example.
I thought naively that it would be sufficient to inherit from Mixin to endow A-type objects with the mixin methods, but the attempts below don't work:
class A: pass
class A_factory:
def __new__(self):
return A()
class Mixin:
def method(self):
print('aha!')
class A_v2(Mixin): # attempt 1
def __new__(cls):
return A_factory()
class A_v3(Mixin): # attempt 2
def __new__(cls):
self = A_factory()
super().__init__(self)
return self
In fact A_v2().method() and A_v3().method() raises AttributeError: 'A' object has no attribute 'method'.
What is the correct way of using A_factory within class A_vn(Mixin) so that A-type objects created by the factory inherit the mixin methods?
There's no obvious reason why you should need __new__ for what you're showing here. There's a nice discussion here on the subject: Why is __init__() always called after __new__()?
If you try the below it should work:
class Mixin:
def method(self):
print('aha!')
class A(Mixin):
def __init__(self):
super().__init__()
test = A()
test.method()
If you need to use a factory method, it should be a function rather than a class. There's a very good discussion of how to use factory methods here: https://realpython.com/factory-method-python/
I am trying to create a simple abstract base class Abstract that along with its own methods provides the methods of two others abstract base classes: Publisher and Subscriber. When I try to initialize the concrete class Concrete, built on Abstract I get this error: Cannot create a consistent method resolution order (MRO) for bases ABC, Publisher, Subscriber. What is the right way to do it?
from abc import ABC, abstractmethod
class Publisher(ABC):
subscribers = set()
def register(self, obj):
self.subscribers.add(obj)
def unregister(self, obj):
self.subscribers.remove(obj)
def dispatch(self, event):
print("dispatching", event)
class Subscriber(ABC):
#abstractmethod
def handle_event(self, event):
raise NotImplementedError
class Abstract(ABC, Publisher, Subscriber):
#abstractmethod
def do_something(self, event):
raise NotImplementedError
class Concrete(Abstract):
def handle_event(self, event):
print("handle_event")
def do_something(self, event):
print("do_something")
c = Concrete()
Abstract classes don't have to have abc.ABC in their list of bases. They have to have abc.ABCMeta (or a descendant) as their metaclass, and they have to have at least one abstract method (or something else that counts, like an abstract property), or they'll be considered concrete. (Publisher has no abstract methods, so it's actually concrete.)
Inheriting from ABC is just a way to get ABCMeta as your class's metaclass, for people more comfortable with inheritance than metaclasses, but it's not the only way. You can also inherit from another class with ABCMeta as its metaclass, or specify metaclass=ABCMeta explicitly.
In your case, inheriting from Publisher and Subscriber will already set Abstract's metaclass to ABCMeta, so inheriting from ABC is redundant. Remove ABC from Abstract's base class list, and everything should work.
Alternatively, if you really want ABC in there for some reason, you can move it to the end of the base class list, which will resolve the MRO conflict - putting it first says you want ABC methods to override methods from the other classes, which conflicts with the fact that the other classes are subclasses of ABC.
Change from this:
class Abstract(ABC, Publisher, Subscriber):
To this:
class Abstract(Publisher, Subscriber):
The two subclasses are already abstract, thus you don't need to inherit from ABC again.
Suppose you have the following class:
class Base(object):
def abstract_method(self):
raise NotImplementedError
Can you then implement a inheriting class, which does not implement the abstract method? For example, when it does not need that specific method. Will that give problems or is it just bad practice?
If you're implementing abstract methods the way you show, there's nothing enforcing the abstractness of the class as a whole, only of the methods that don't have a concrete definition. So you can create an instance of Base, not only of its subclasses.
b = Base() # this works following your code, only b.abstract_method() raises
def Derived(Base):
... # no concrete implementation of abstract_method, so this class works the same
However, if you use the abc module from the standard library to designate abstract methods, it will not allow you to instantiate an instance of any class that does not have a concrete implementation of any abstract methods it has inherited. You can leave inherited abstract methods unimplemented in an intermediate abstract base class (e.g. a subclass of the original base, that is itself intended to still be abstract), but you can't make any instances.
Here's what using abc looks like:
from abc import ABCMeta, abstractmethod
class ABCBase(metaclass=ABCMeta):
#abstractmethod
def abstract_method(self, arg):
...
class ABCDerived(ABCBase):
... # we don't implement abstract_method here, so we're also an abstract class
d = ABCDerived() # raises an error
I am developing a system, which has a series of single multilevel inheritance hierarachy. one of the methods (applicable to all the classes) has to perform the same thing for most of the classes, which is to pass a list to its parent class.
I know that if one doesn't define a method in one of the inherited classes, its parents' methods are used. But when we use the super method, we need to mention the name of the class being called.
One method I know to achieve this is to redefine the method at every class with class name as argument. Is there any elegant method where I can define it once at the topmost parent, and then override it only when necessary?
The implementation right now looks like this
class a(object):
def __init__(self):
self.myL = list()
print 'hello'
class b(a):
def __init__(self):
super(b,self).__init__()
def resolve(self, passVal):
print passVal
self.myL.append(passVal)
super(b,self).resolve(passVal+1)
class c(b):
def __init__(self):
super(c,self).__init__()
def resolve(self, passVal):
print passVal
self.myL.append(passVal)
super(c,self).resolve(passVal+1)
Instead if I can define resolve in class a, and then all other classes inherit the method from it. I understand a will never be able to use it. but redefining the method seems a lot unnecessary extra work.