From the docs:
A class that has a metaclass derived from
ABCMetacannot be instantiated unless all of its abstract methods and properties are overridden.
Conversely, this means that any class with no abstract methods or properties like your AbstractClass can be instantiated.
If you want to disallow instantiation of the topmost parent class, you can write a custom class that performs a type check in its __new__ method:
class SubclassOnlyABC(object):
__metaclass__ = abc.ABCMeta
def __new__(cls, *args, **kwargs):
if cls.__bases__ == (SubclassOnlyABC,):
msg = 'Abstract class {} cannot be instantiated'.format(cls.__name__)
raise TypeError(msg)
return super(SubclassOnlyABC, cls).__new__(cls, *args, **kwargs)
class AbstractClass(SubclassOnlyABC):
pass
class ChildClass(AbstractClass):
pass
ChildClass() # works because it's a child class of an abstract class
AbstractClass() # throws TypeError because its parent class is "object"
You can also write a __new__ method that prevents instantiation of classes with no abstract methods:
class NonEmptyABC(object):
__metaclass__ = abc.ABCMeta
def __new__(cls, *args, **kwargs):
# check if ANY abstractmethod exists
for parentcls in cls.__mro__:
if any(getattr(attr, '__isabstractmethod__', False)
for attr in vars(parentcls).values()):
break
else:
msg = 'Abstract class {} cannot be instantiated'.format(cls.__name__)
raise TypeError(msg)
return super(NonEmptyABC, cls).__new__(cls, *args, **kwargs)
class EmptyAbstractClass(NonEmptyABC):
pass
class NonemptyAbstractClass(NonEmptyABC):
@abc.abstractmethod
def foo(self):
pass
class NonemptyChild(NonemptyAbstractClass):
def foo(self):
pass
NonemptyChild() # works because "foo" is an abstractmethod
EmptyAbstractClass() # throws TypeError because there are no abstractmethods
Answer from Aran-Fey on Stack OverflowHello. In this first example, I have a short and straightforward code w/ a class for interface. It doesn't inherit from ABC and doesn't have any abstract methods.
class Abs():
def __init__(self, name, age):
self.name = name
self.age = age
def go_to(self):
return f"{self.name} is going to {self.place}."
class Teacher(Abs):
place = "work"
class Student(Abs):
place = "school"
t1 = Teacher("James", 56)
s1 = Student("Tim", 15)
print(t1.go_to())
print(s1.go_to())In this second example, it's the exact opposite.
from abc import ABC, abstractmethod
class Abs(ABC):
def __init__(self, name, age):
self.name = name
self.age = age
@abstractmethod
def go_to(self):
...
class Teacher(Abs):
place = "work"
def go_to(self):
return f"{self.name} is going to {self.place}."
class Student(Abs):
place = "school"
def go_to(self):
return f"{self.name} is going to {self.place}."
t1 = Teacher("James", 56)
s1 = Student("Tim", 15)
print(t1.go_to())
print(s1.go_to())Both examples have the same output. In the tutorials/articles I've read, most times the second example is preferred. In the abstract class, abstract methods get defined and decorated, and then in the inheriting classes they all get redefined with the rest of the logic. What's the point of creating a class w/ abstract methods which later on we redefine? What issue does that solve? Why not just proceed as in the first example - simple, less code, one parent class for the interface, if we need to add other details, we do so in the base class once and handle the extra logic with that additional info there. Doesn't the first code present a better example of loose coupling - just one connection between parent and child classes, where in the second code, we get connections between parent/child in every method that we redefine? I feel like I'm missing something, because to me, the second example is much more spaghetti-like. If anyone can explain why it's a good practice to redefine abstract methods that would be nice. Also, is it a bad practice to write code as in the first example, w/o ABC+@abstractmethod in the parent class?
Thanks.
From the docs:
A class that has a metaclass derived from
ABCMetacannot be instantiated unless all of its abstract methods and properties are overridden.
Conversely, this means that any class with no abstract methods or properties like your AbstractClass can be instantiated.
If you want to disallow instantiation of the topmost parent class, you can write a custom class that performs a type check in its __new__ method:
class SubclassOnlyABC(object):
__metaclass__ = abc.ABCMeta
def __new__(cls, *args, **kwargs):
if cls.__bases__ == (SubclassOnlyABC,):
msg = 'Abstract class {} cannot be instantiated'.format(cls.__name__)
raise TypeError(msg)
return super(SubclassOnlyABC, cls).__new__(cls, *args, **kwargs)
class AbstractClass(SubclassOnlyABC):
pass
class ChildClass(AbstractClass):
pass
ChildClass() # works because it's a child class of an abstract class
AbstractClass() # throws TypeError because its parent class is "object"
You can also write a __new__ method that prevents instantiation of classes with no abstract methods:
class NonEmptyABC(object):
__metaclass__ = abc.ABCMeta
def __new__(cls, *args, **kwargs):
# check if ANY abstractmethod exists
for parentcls in cls.__mro__:
if any(getattr(attr, '__isabstractmethod__', False)
for attr in vars(parentcls).values()):
break
else:
msg = 'Abstract class {} cannot be instantiated'.format(cls.__name__)
raise TypeError(msg)
return super(NonEmptyABC, cls).__new__(cls, *args, **kwargs)
class EmptyAbstractClass(NonEmptyABC):
pass
class NonemptyAbstractClass(NonEmptyABC):
@abc.abstractmethod
def foo(self):
pass
class NonemptyChild(NonemptyAbstractClass):
def foo(self):
pass
NonemptyChild() # works because "foo" is an abstractmethod
EmptyAbstractClass() # throws TypeError because there are no abstractmethods
I usually just declare the base class's __init__ with @abc.abstractmethod.
If my base class does not have an __init__, I add a trivial one.
Something like this:
class AbstractClass(abc.ABC):
@abc.abstractmethod
def __init__(self):
pass
# other useful non-abstract methods...
class ChildClass(AbstractClass):
def __init__(self):
pass
if __name__ == '__main__':
child = ChildClass() # allowed
abstract = AbstractClass() # TypeError
Videos
Use the abc module to create abstract classes. Use the abstractmethod decorator to declare a method abstract, and declare a class abstract using one of three ways, depending upon your Python version.
In Python 3.4 and above, you can inherit from ABC. In earlier versions of Python, you need to specify your class's metaclass as ABCMeta. Specifying the metaclass has different syntax in Python 3 and Python 2. The three possibilities are shown below:
Copy# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
@abstractmethod
def foo(self):
pass
Copy# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
@abstractmethod
def foo(self):
pass
Copy# Python 2
from abc import ABCMeta, abstractmethod
class Abstract:
__metaclass__ = ABCMeta
@abstractmethod
def foo(self):
pass
Whichever way you use, you won't be able to instantiate an abstract class that has abstract methods, but will be able to instantiate a subclass that provides concrete definitions of those methods:
>>> Abstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class Abstract with abstract methods foo
>>> class StillAbstract(Abstract):
... pass
...
>>> StillAbstract()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class StillAbstract with abstract methods foo
>>> class Concrete(Abstract):
... def foo(self):
... print('Hello, World')
...
>>> Concrete()
<__main__.Concrete object at 0x7fc935d28898>
The old-school (pre-PEP 3119) way to do this is just to raise NotImplementedError in the abstract class when an abstract method is called.
Copyclass Abstract(object):
def foo(self):
raise NotImplementedError('subclasses must override foo()!')
class Derived(Abstract):
def foo(self):
print 'Hooray!'
>>> d = Derived()
>>> d.foo()
Hooray!
>>> a = Abstract()
>>> a.foo()
Traceback (most recent call last): [...]
This doesn't have the same nice properties as using the abc module does. You can still instantiate the abstract base class itself, and you won't find your mistake until you call the abstract method at runtime.
But if you're dealing with a small set of simple classes, maybe with just a few abstract methods, this approach is a little easier than trying to wade through the abc documentation.
Before abc was introduced you would see this frequently.
class Base(object):
def go(self):
raise NotImplementedError("Please Implement this method")
class Specialized(Base):
def go(self):
print "Consider me implemented"
Something along these lines, using ABC
import abc
class Shape(object):
__metaclass__ = abc.ABCMeta
@abc.abstractmethod
def method_to_implement(self, input):
"""Method documentation"""
return
Also read this good tutorial: https://pymotw.com/3/abc/
You can also check out zope.interface which was used prior to introduction of ABC in python.
- http://pypi.python.org/pypi/zope.interface
- https://zopeinterface.readthedocs.io/en/latest/README.html
Are you using python3 to run that code? If yes, you should know that declaring metaclass in python3 have changes you should do it like this instead:
import abc
class AbstractClass(metaclass=abc.ABCMeta):
@abc.abstractmethod
def abstractMethod(self):
return
The full code and the explanation behind the answer is:
import abc
class AbstractClass(metaclass=abc.ABCMeta):
@abc.abstractmethod
def abstractMethod(self):
return
class ConcreteClass(AbstractClass):
def __init__(self):
self.me = "me"
# Will get a TypeError without the following two lines:
# def abstractMethod(self):
# return 0
c = ConcreteClass()
c.abstractMethod()
If abstractMethod is not defined for ConcreteClass, the following exception will be raised when running the above code: TypeError: Can't instantiate abstract class ConcreteClass with abstract methods abstractMethod
Import ABC from abc and make your own abstract class a child of ABC can help make the code look cleaner.
from abc import ABC, abstractmethod
class AbstractClass(ABC):
@abstractmethod
def abstractMethod(self):
return
class ConcreteClass(AbstractClass):
def __init__(self):
self.me = "me"
# The following would raise a TypeError complaining
# abstractMethod is not implemented
c = ConcreteClass()
Tested with Python 3.6
What you're trying to do will just work—but it's a very bad idea.
In general, you don't want to change the signature of a method in incompatible ways when overriding. That's part of the Liskov Substitution Principle.
In Python, there are often good reasons to violate that—inheritance isn't always about subtyping.
But when you're using ABCs to define an interface, that's explicitly about subtyping. That's the sole purpose of ABC subclasses and abstractmethod decorators, so using them to mean anything else is at best highly misleading.
In more detail:
By inheriting from Agent, you are declaring that any instance of Clever_Agent can be used as if it were an Agent. That includes being able to call my_clever_agent.perceive_world(my_observation). In fact, it doesn't just include that; that's the entirely of what it means! If that call will always fail, then no Clever_Agent is an Agent, so it shouldn't claim to be.
In some languages, you occasionally need to fake your way around interface checking, so you can later type-switch and/or "dynamic-cast" back to the actual type. But in Python, that's never necessary. There's no such thing as "a list of Agents", just a list of anything-at-alls. (Unless you're using optional static type checking—but in that case, if you need to get around the static type checking, don't declare a static type just to give yourself a hurdle to get around.)
In Python, you can extend a method beyond its superclass method by adding optional parameters, and that's perfectly valid, because it's still compatible with the explicitly-declared type. For example, this would be a perfectly reasonable thing to do:
class Clever_Agent(Agent):
def perceive_world(self, observation, prediction=None):
print('I see %s' % observation)
if prediction is None:
print('I have no predictions about what will happen next')
else:
print('I think I am going to see %s happen next' % prediction)
Or even this might be reasonable:
class Agent(ABC):
@abstractmethod
def perceive_world(self, observation, prediction):
pass
class Dumb_agent(Agent):
def perceive_world(self, observation, prediction=None):
print('I see %s' % observation)
if prediction is not None:
print('I am too dumb to make a prediction, but I tried anyway')
class Clever_Agent(Agent):
def perceive_world(self, observation, prediction):
print('I see %s' % observation)
print('I think I am going to see %s happen next' % prediction)
In many ways overriding an abstract method from a parent class and adding or changing the method signature is technically not called a method override what you may be effectively be doing is method hiding. Method override always overrides a specific existing method signature in the parent class.
You may find your way around the problem by defining a variant abstract method in your parent class, and overriding it if necessary in your sub classes.
I was surprised to see the Python abstract classes don't enforce anything except the override and method name. I can see why in Python enforcing parameter data-types would probably not work, but the number of parameters and parameter names ought to be enforced.
I've always thought the point of abstract classes was to ensure that any inheritor of the class would would work with existing code to run the abstract methods defined in the super class. The whole point was to enforce method signatures.
It seems to me that Python's implantation of abstract classes has very little utility. Does anyone even use them? What for?