from abc import ABC, abstractmethod
class A(ABC):
def __init__(self, name):
self.name = name
#abstractmethod
def something():
pass
class B(A):
pass
I'm still new to learning OOP so I would like to ask this. I know that an abstract class is considered a superclass, but can an abstract class be a subclass as well?
Using the code as an example, B inherits from A but does not override the abstract methods in A, so does this mean that B is still considered an abstract class as well as as a subclass of A?
First, what is an abstract class? It is a class that is to be used as a "skeleton" for a subclass.
Now to your question....
So does this mean that B is still considered an abstract class as well as as a subclass of A?
Yes, because all of the methods are not overridden, and they are abstract.
Take this code for example:
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def foo(self):
pass
#abstractmethod
def bar(self):
pass
class B(A):
def foo(self):
return 'Foo implementation'
Here, the class B is still abstract since one of the methods is not overridden that is abstract. So if you try to create an instance of that, you'll get this error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class B with abstract methods bar
Here we can see that this class inherits from A, but is a concrete class, since the methods are overridden:
class C(A):
def foo(self):
pass
def bar(self):
pass
c = C()
c.foo()
This code runs without errors.
In short, a subclass of an abstract class is still an abstract class as long as the methods are not overridden.
Yes. As long as you don't override all the abstract methods, the subclass is still abstract.
Related
Two mixin classes specify requirements as abstract methods. Together, the classes have a full set of concrete methods. However, they fail to combine into a concrete class: no matter which order I use to declare the concrete class, some abstract methods override the concrete ones.
Is there a way to prevent abstract methods from overriding the concrete methods? I believe this works in Scala for example.
What are alternative ways to specify the requirements?
Here is a concrete example:
import abc
class A(abc.ABC):
#abc.abstractmethod
def x(self):
pass
def y(self):
return "A.y"
class B(abc.ABC):
#abc.abstractmethod
def y(self):
pass
def x(self):
return f"B.x"
class AB(B, A):
pass
class BA(A, B):
pass
ab = AB() # TypeError: Can't instantiate abstract class AB with abstract methods y
ba = BA() # TypeError: Can't instantiate abstract class BA with abstract methods x
Final class user might want to create a a class composed from Base and Mixin (Mixin provides additional common functionality over a 3rd party library classes).
But the Mixin.__init__ is not called when used as below. Only Base.__init__ is called:
>>> class Base(object): #3rd party library class
... def __init__(self): print "Base"
...
>>> class Mixin(object): #my features useful as addendum for a few classes
... def __init__(self): print "Mixin"
...
>>> class C(Base, Mixin): pass
...
>>> c = C()
Base
How to enforce calling both Mixin.__init__ and Base.__init__ in this scenario without requiring the user to remember to put a constructor with super() call in C class?
>>> class Base(object):
... def __init__(self): print "Base"
...
>>> class Mixin(object):
... def __init__(self): print "Mixin"
...
>>> class C(Base, Mixin):
... #easy to forget to add constructor
... def __init__(self): super(C, self).__init__()
...
>>> c = C()
Base
Mixin
Python doesn't chain any method calls like this automatically, so you need to be disciplined and use cooperative inheritance correctly. If one class uses super, then all classes must use super. (This is a simple case, since none of the overriden __init__ methods add any additional arguments. If they did, you would need to do some work to ensure that object.__init__ never received additional arguments once it was called.)
Read https://rhettinger.wordpress.com/2011/05/26/super-considered-super/ before continuing.
class Base(object):
def __init__(self):
print "Base"
super(Base, self).__init__()
class Mixin(object):
def __init__(self):
print "Mixin"
super(Mixin, self).__init__()
class C(Base, Mixin):
pass
c = C()
(This is also a perfect example of why you need to understand how super works, and not treat it as simple indirect reference to your class's base class.)
If you can't modify Base or Mixin to use super, then you'll need to define wrappers around them that can. The linked article explains how.
I have the following class hierarchy:
class AbstractClass(object):
__metaclass__ = ABCMeta
#abstractmethod
def foo(self):
pass
class A(AbstractClass):
def __init__():
super().__init__()
def foo(self):
//Logic
class B(A):
def __init__():
super().__init__()
I want to use foo as it is implemented in A, so I cannot override it in B.
Using B.foo() works, but I still get the warning from PyCharm:
"Class B must implement all abstract methods"
Do I have to override a method that already overrides an abstract method? How do I override it without losing the implementation? Just copy the method to the sub class?
I was just going to ask this question when I suddenly had an idea how it could work. I thought "how I can call a method after I just overrode it?"
After some thought I finally figured it out.
Call the overridden method from the super class while overriding it in the sub class:
class B(A):
def __init__():
super().__init__()
def foo(self):
super().foo()
This works because a supertypes method must work with its subtypes without further implementation provided. After I figured it out it just seems so logical.
This might be useful for people who are just figuring out how inheritance works.
I'm trying to understand the benefits of using abstract base classes. Consider these two pieces of code:
Abstract base class:
from abc import ABCMeta, abstractmethod, abstractproperty
class CanFly:
__metaclass__ = ABCMeta
#abstractmethod
def fly(self):
pass
#abstractproperty
def speed(self):
pass
class Bird(CanFly):
def __init__(self):
self.name = 'flappy'
#property
def speed(self):
return 1
def fly(self):
print('fly')
b = Bird()
print(isinstance(b, CanFly)) # True
print(issubclass(Bird, CanFly)) # True
Plain inheritance:
class CanFly(object):
def fly(self):
raise NotImplementedError
#property
def speed(self):
raise NotImplementedError()
class Bird(CanFly):
#property
def speed(self):
return 1
def fly(self):
print('fly')
b = Bird()
print(isinstance(b, CanFly)) # True
print(issubclass(Bird, CanFly)) # True
As you see, both methods support inflection using isinstance and issubclass.
Now, one difference I know is that, if you try to instantiate a subclass of an abstract base class without overriding all abstract methods/properties, your program will fail loudly. However, if you use plain inheritance with NotImplementedError, your code won't fail until you actually invoke the method/property in question.
Other than that, what makes using abstract base class different?
The most notable answer in terms of concrete specifics, besides what you mentioned in your question, is that the presence of the #abstractmethod or #abstractproperty1 decorators, along with inheriting from ABC (or having the ABCMeta metaclass) prevents you from instantiating the object at all.
from abc import ABC, abstractmethod
class AbsParent(ABC):
#abstractmethod
def foo(self):
pass
AbsParent()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class AbsParent with abstract methods foo
However, there's more at play here. Abstract Base Classes were introduced to Python in PEP 3119. I'd recommend reading through the "Rationale" section for Guido's take on why they were introduced in the first place. My sophomoric summary would be that they're less about their concrete features and more about their philosophy. Their purpose is to signal to external inspectors that the object is inheriting from the ABC, and because it's inheriting from an ABC it will follow a good-faith agreement. This "good-faith agreement" is that the child object will follow the intention of the parent. The actual implementation of this agreement is left up to you, which is why it's a good-faith agreement, and not an explicit contract.
This primarily shows up through the lens of the register() method. Any class that has ABCMeta as its metaclass (or simply inherits from ABC) will have a register() method on it. By registering a class with an ABC you are signaling that it inherits from the ABC, even though it technically doesn't. This is where the good-faith agreement comes in.
from abc import ABC, abstractmethod
class MyABC(ABC):
#abstractmethod
def foo(self):
"""should return string 'foo'"""
pass
class MyConcreteClass(object):
def foo(self):
return 'foo'
assert not isinstance(MyConcreteClass(), MyABC)
assert not issubclass(MyConcreteClass, MyABC)
While MyConcreteClass, at this point is unrelated to MyABC, it does implement the API of MyABC according to the requirements laid out in the comments. Now, if we register MyConcreteClass with MyABC, it will pass isinstance and issubclass checks.
MyABC.register(MyConcreteClass)
assert isinstance(MyConcreteClass(), MyABC)
assert issubclass(MyConcreteClass, MyABC)
Again, this is where the "good-faith agreement" comes into play. You do not have to follow the API laid out in MyABC. By registering the concrete class with the ABC we are telling any external inspectors that we, the programmers, are adhering to the API we're supposed to.
1 note that #abstractproperty is no longer preferred. Instead you should use:
#property
#abstractmethod
def foo(self):
pass
I initially defined the following abstract class:
from abc import ABC, abstractmethod
class Primitive(ABC):
Now I want to create another abstract class that inherits from Primitive:
class InstrumentName(Primitive)
I need this class to be abstract since I ultimately want to create the following two concrete classes:
class CurrencyInstrumentName(InstrumentName)
class MetalInstrumentName(InstrumentName)
I have read the documentation and searched SO, but they mostly pertain to sublcassing concrete classes from abstract classes, or discussing how Python handles abstraction
Just subclass, you don't need to do anything special.
A class only becomes concrete when there are no more abstractmethod and abstractproperty objects left in the implementation.
Let's illustrate this:
from abc import ABC, abstractmethod
class Primitive(ABC):
#abstractmethod
def foo(self):
pass
#abstractmethod
def bar(self):
pass
class InstrumentName(Primitive):
def foo(self):
return 'Foo implementation'
Here, InstrumentName is still abstract, because bar is left as an abstractmethod. You can't create an instance of that subclass:
>>> InstrumentName()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class InstrumentName with abstract methods bar
Subclasses can also add #abstractmethod or #abstractproperty methods as needed.
Under the hood, all subclasses inherit the ABCMeta metaclass that enforces this, and it simply checks if there are any #abstractmethod or #abstractproperty attributes left on the class.
As #MartijnPieters wrote, you don't need to do anything special for Python, but PyCharm will warn:
Class InstrumentName must implement all abstract methods
One way to suppress that warning:
import abc
class Primitive(abc.ABC):
#abc.abstractmethod
def foo(self):
pass
# noinspection PyAbstractClass
class InstrumentName(Primitive):
def is_tuba(self):
return False
Another way:
...
class InstrumentName(Primitive, metaclass=abc.ABCMeta):
def is_tuba(self):
return False