461

How can I make a class or method abstract in Python?

I tried redefining __new__() like so:

class F:
    def __new__(cls):
        raise Exception("Unable to create an instance of abstract class %s" %cls)

But now, if I create a class G that inherits from F like so:

class G(F):
    pass

Then, I can't instantiate G either, since it calls its super class's __new__ method.

Is there a better way to define an abstract class?

1
  • 3
    Yes, you can create abstract classes in python with the abc (abstract base classes) module. This site will help you with it: http://docs.python.org/2/library/abc.html Commented Nov 30, 2012 at 13:32

13 Answers 13

761

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:

# Python 3.4+
from abc import ABC, abstractmethod
class Abstract(ABC):
    @abstractmethod
    def foo(self):
        pass
# Python 3.0+
from abc import ABCMeta, abstractmethod
class Abstract(metaclass=ABCMeta):
    @abstractmethod
    def foo(self):
        pass
# 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>
Sign up to request clarification or add additional context in comments.

13 Comments

what does the @abstractmethod do? Why do you need it? If the class is already established as abstract shouldn't the compiler/interpret know that all the methods are from the abstract class in question?
@CharlieParker - The @abstractmethod makes it so that the decorated function must be overridden before the class can be instantiated. From the docs: A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden.
@CharlieParker - Basically, it lets you define a class in a manner where the sub-class must implement a specified suite of methods to instantiate at all.
Is there a way to prohibit users from creating Abstract() without any @abstractmethod methods?
Why can't I use only @abstractmethod without ABC? It feels unpythonic. When I write abstractmethod it means that this is an abstract base class already. Or do I miss sth?
|
152

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.

class 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.

4 Comments

Appreciate the simplicity and effectiveness of this approach.
Haha, I saw this everywhere in my OMSCS course and had no clue what it was :)
It isn't really helpful, though. Because you might want to implement some common behavior inside Abstract#foo. Calling it directly should be forbidden, but it should still be possible to call it with super().
Nice little light weight alternative solution for use-cases that don't feel threatened by it.
30

Here's a very easy way without having to deal with the ABC module.

In the __init__ method of the class that you want to be an abstract class, you can check the "type" of self. If the type of self is the base class, then the caller is trying to instantiate the base class, so raise an exception. Here's a simple example:

class Base():
    def __init__(self):
        if type(self) is Base:
            raise Exception('Base is an abstract class and cannot be instantiated directly')
        # Any initialization code
        print('In the __init__  method of the Base class')

class Sub(Base):
    def __init__(self):
        print('In the __init__ method of the Sub class before calling __init__ of the Base class')
        super().__init__()
        print('In the __init__ method of the Sub class after calling __init__ of the Base class')

subObj = Sub()
baseObj = Base()

When run, it produces:

In the __init__ method of the Sub class before calling __init__ of the Base class
In the __init__  method of the Base class
In the __init__ method of the Sub class after calling __init__ of the Base class
Traceback (most recent call last):
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 16, in <module>
    baseObj = Base()
  File "/Users/irvkalb/Desktop/Demo files/Abstract.py", line 4, in __init__
    raise Exception('Base is an abstract class and cannot be instantiated directly')
Exception: Base is an abstract class and cannot be instantiated directly

This shows that you can instantiate a subclass that inherits from a base class, but you cannot instantiate the base class directly.

1 Comment

Another interesting alternative.
24

Most Previous answers were correct but here is the answer and example for Python 3.7. Yes, you can create an abstract class and method. Just as a reminder sometimes a class should define a method which logically belongs to a class, but that class cannot specify how to implement the method. For example, in the below Parents and Babies classes they both eat but the implementation will be different for each because babies and parents eat a different kind of food and the number of times they eat is different. So, eat method subclasses overrides AbstractClass.eat.

from abc import ABC, abstractmethod

class AbstractClass(ABC):

    def __init__(self, value):
        self.value = value
        super().__init__()

    @abstractmethod
    def eat(self):
        pass

class Parents(AbstractClass):
    def eat(self):
        return "eat solid food "+ str(self.value) + " times each day"

class Babies(AbstractClass):
    def eat(self):
        return "Milk only "+ str(self.value) + " times or more each day"

food = 3    
mom = Parents(food)
print("moms ----------")
print(mom.eat())

infant = Babies(food)
print("infants ----------")
print(infant.eat())

OUTPUT:

moms ----------
eat solid food 3 times each day
infants ----------
Milk only 3 times or more each day

5 Comments

Can we write @abstractmethod on init function also?
+1 Why @abstractmethod def eat(self)? If this class is abstract and hence not meant to be instantiated why do you pass (self) to eat? It works fine without it
Because in Python: - Instance methods always take self as the first argument (reference to the instance). - If you remove self, you are defining a function, not a proper instance method. If you go mom.eat() when there is no self list of parameters, then you'll get a TypeError, as the interpreter is expecting the instance as the first argument.
Why is a call to super().__init__() needed in your AbstractClass constructor?
Unlike some languages (like Java), Python does not automatically call every base class’s constructor. Although super().__init__() isn't needed for ABC to work, you could argue that it's good practice to include it, as it could be useful if you decide to include multiple inheritance at a later stage, and you want to run all the parent constructors.
18

As explained in the other answers, yes you can use abstract classes in Python using the abc module. Below I give an actual example using abstract @classmethod, @property and @abstractmethod (using Python 3.6+). For me it is usually easier to start off with examples I can easily copy&paste; I hope this answer is also useful for others.

Let's first create a base class called Base:

from abc import ABC, abstractmethod

class Base(ABC):

    @classmethod
    @abstractmethod
    def from_dict(cls, d):
        pass
    
    @property
    @abstractmethod
    def prop1(self):
        pass

    @property
    @abstractmethod
    def prop2(self):
        pass

    @prop2.setter
    @abstractmethod
    def prop2(self, val):
        pass

    @abstractmethod
    def do_stuff(self):
        pass

Our Base class will always have a from_dict classmethod, a property prop1 (which is read-only) and a property prop2 (which can also be set) as well as a function called do_stuff. Whatever class is now built based on Base will have to implement all of these four methods/properties. Please note that for a method to be abstract, two decorators are required - classmethod and abstract property.

Now we could create a class A like this:

class A(Base):
    def __init__(self, name, val1, val2):
        self.name = name
        self.__val1 = val1
        self._val2 = val2

    @classmethod
    def from_dict(cls, d):
        name = d['name']
        val1 = d['val1']
        val2 = d['val2']

        return cls(name, val1, val2)

    @property
    def prop1(self):
        return self.__val1

    @property
    def prop2(self):
        return self._val2

    @prop2.setter
    def prop2(self, value):
        self._val2 = value

    def do_stuff(self):
        print('juhu!')

    def i_am_not_abstract(self):
        print('I can be customized')

All required methods/properties are implemented and we can - of course - also add additional functions that are not part of Base (here: i_am_not_abstract).

Now we can do:

a1 = A('dummy', 10, 'stuff')
a2 = A.from_dict({'name': 'from_d', 'val1': 20, 'val2': 'stuff'})

a1.prop1
# prints 10

a1.prop2
# prints 'stuff'

As desired, we cannot set prop1:

a.prop1 = 100

will return

AttributeError: can't set attribute

Also our from_dict method works fine:

a2.prop1
# prints 20

If we now defined a second class B like this:

class B(Base):
    def __init__(self, name):
        self.name = name

    @property
    def prop1(self):
        return self.name

and tried to instantiate an object like this:

b = B('iwillfail')

we will get an error

TypeError: Can't instantiate abstract class B with abstract methods do_stuff, from_dict, prop2

listing all the things defined in Base which we did not implement in B.

3 Comments

Note that there is an @abstactclassmethod annotation which you can use instead of two separate ones
@FishingIsLife: According to the documentation @abstractclassmethod is deprecated as of version 3.3, if I understand it correctly.
you are right. Thanks for the hint.
9

This one will be working in python 3

from abc import ABCMeta, abstractmethod

class Abstract(metaclass=ABCMeta):

    @abstractmethod
    def foo(self):
        pass

Abstract()
>>> TypeError: Can not instantiate abstract class Abstract with abstract methods foo

1 Comment

At present we can just from abc import ABC and class MyABC(ABC).
4

You can also harness the __new__ method to your advantage. You just forgot something. The __new__ method always returns the new object so you must return its superclass' new method. Do as follows.

class F:
    def __new__(cls):
        if cls is F:
            raise TypeError("Cannot create an instance of abstract class '{}'".format(cls.__name__))
        return super().__new__(cls)

When using the new method, you have to return the object, not the None keyword. That's all you missed.

Comments

4

You can create an abstract class by extending ABC which stands for "Abstract Base Classes" and can create the abstract method with @abstractmethod in the abstract class as shown below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

And, to use an abstract class, it should be extended by a child class and the child class should override the abstract method of the abstract class as shown below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

class Cat(Animal): # Extends "Animal" abstract class
    def sound(self): # Overrides "sound()" abstract method
        print("Meow!!")

obj = Cat()
obj.sound()

Output:

Meow!!

And, an abstract method can have code rather than pass and can be called by a child class as shown below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        print("Wow!!") # Here

class Cat(Animal):
    def sound(self):
        super().sound() # Here
        
obj = Cat()
obj.sound()

Output:

Wow!!

And, an abstract class can have the variables and non-abstract methods which can be called by a child class and non-abstract methods don't need to be overridden by a child class as shown below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass
    
    def __init__(self): # Here
        self.name = "John" # Here
    
    x = "Hello" # Here
    
    def test1(self): # Here
        print("Test1")
    
    @classmethod # Here
    def test2(cls):
        print("Test2")
        
    @staticmethod # Here
    def test3():
        print("Test3")

class Cat(Animal):
    def sound(self):
        print(self.name) # Here
        print(super().x) # Here
        super().test1()  # Here
        super().test2()  # Here
        super().test3()  # Here

obj = Cat()
obj.sound()

Output:

John
Hello
Test1
Test2
Test3

And, you can define an abstract class and static methods and an abstract getter, setter and deleter in an abstract class as shown below. *@abstractmethod must be the innermost decorator otherwise error occurs and you can see my answer which explains more about an abstract getter, setter and deleter:

from abc import ABC, abstractmethod

class Person(ABC):

    @classmethod
    @abstractmethod # The innermost decorator
    def test1(cls):
        pass
    
    @staticmethod
    @abstractmethod # The innermost decorator
    def test2():
        pass

    @property
    @abstractmethod # The innermost decorator
    def name(self):
        pass

    @name.setter
    @abstractmethod # The innermost decorator
    def name(self, name):
        pass

    @name.deleter
    @abstractmethod # The innermost decorator
    def name(self):
        pass

Then, you need to override them in a child class as shown below:

class Student(Person):
    
    def __init__(self, name):
        self._name = name
    
    @classmethod
    def test1(cls): # Overrides abstract class method
        print("Test1")
    
    @staticmethod
    def test2(): # Overrides abstract static method
        print("Test2")
    
    @property
    def name(self): # Overrides abstract getter
        return self._name
    
    @name.setter
    def name(self, name): # Overrides abstract setter
        self._name = name
    
    @name.deleter
    def name(self): # Overrides abstract deleter
        del self._name

Then, you can instantiate the child class and call them as shown below:

obj = Student("John") # Instantiates "Student" class
obj.test1() # Class method
obj.test2() # Static method
print(obj.name) # Getter
obj.name = "Tom" # Setter
print(obj.name) # Getter
del obj.name # Deleter
print(hasattr(obj, "name"))

Output:

Test1
Test2
John 
Tom  
False

And, if you try to instantiate an abstract class as shown below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

obj = Animal()

The error below occurs:

TypeError: Can't instantiate abstract class Animal with abstract methods sound

And, if you don't override the abstract method of an abstract class in a child class and you instantiate the child class as shown below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

class Cat(Animal):
    pass # Doesn't override "sound()" abstract method

obj = Cat() # Here

The error below occurs:

TypeError: Can't instantiate abstract class Cat with abstract methods sound

And, if you define an abstract method in the non-abstract class which doesn't extend ABC, the abstract method is a normal instance method so there are no errors even if the non-abstract class is instantiated and even if a child class doesn't override the abstract method of the non-abstract class as shown below:

from abc import ABC, abstractmethod

class Animal: # Doesn't extend "ABC"
    @abstractmethod # Here
    def sound(self):
        print("Wow!!")

class Cat(Animal):
    pass # Doesn't override "sound()" abstract method

obj1 = Animal() # Here
obj1.sound()

obj2 = Cat() # Here
obj2.sound()

Output:

Wow!!
Wow!!

In addition, you can replace Cat class extending Animal class below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

# ↓↓↓ Here ↓↓↓

class Cat(Animal):
    def sound(self):
        print("Meow!!")

# ↑↑↑ Here ↑↑↑

print(issubclass(Cat, Animal))

With this code having register() below:

from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def sound(self):
        pass

# ↓↓↓ Here ↓↓↓

class Cat:
    def sound(self):
        print("Meow!!")
        
Animal.register(Cat)

# ↑↑↑ Here ↑↑↑

print(issubclass(Cat, Animal))

Then, both of the code above outputs the same result below showing Cat class is the subclass of Animal class:

True

Comments

3

also this works and is simple:

class A_abstract(object):

    def __init__(self):
        # quite simple, old-school way.
        if self.__class__.__name__ == "A_abstract": 
            raise NotImplementedError("You can't instantiate this abstract class. Derive it, please.")

class B(A_abstract):

        pass

b = B()

# here an exception is raised:
a = A_abstract()

Comments

3

I find the accepted answer, and all the others strange, since they pass self to an abstract class. An abstract class is not instantiated so can't have a self.

So try this, it works.

from abc import ABCMeta, abstractmethod


class Abstract(metaclass=ABCMeta):
    @staticmethod
    @abstractmethod
    def foo():
        """An abstract method. No need to write pass"""


class Derived(Abstract):
    def foo(self):
        print('Hooray!')


FOO = Derived()
FOO.foo()

2 Comments

Even abc documentation use self in their examples link
self has a use case: a stub definition that can be called from the overriding method in the child class with super().
3
 from abc import ABCMeta, abstractmethod

 #Abstract class and abstract method declaration
 class Jungle(metaclass=ABCMeta):
     #constructor with default values
     def __init__(self, name="Unknown"):
     self.visitorName = name

     def welcomeMessage(self):
         print("Hello %s , Welcome to the Jungle" % self.visitorName)

     # abstract method is compulsory to defined in child-class
     @abstractmethod
     def scarySound(self):
         pass

1 Comment

Nice @Shivam Bharadwaj , I did it same way
2

Late to answer here, but to answer the other question "How to make abstract methods" which points here, I offer the following.

# decorators.py
def abstract(f):
    def _decorator(*_):
        raise NotImplementedError(f"Method '{f.__name__}' is abstract")
    return _decorator


# yourclass.py
class Vehicle:
    def add_energy():
       print("Energy added!")

    @abstract
    def get_make(): ...

    @abstract
    def get_model(): ...

The class base Vehicle class can still be instantiated for unit testing (unlike with ABC), and the Pythonic raising of an exception is present. Oh yes, you also get the method name that is abstract in the exception with this method for convenience.

1 Comment

I like this, but have a doubt: isn't the contract that an abstract method has to be implemented in a concrete subclass? The (admirable) simplicity of your solution (the poss of testing is a non-trivial aspect of its goodness) means that I can ask that question: it's not clear to me (as yet) whether the abc module solution enforces implementing the designated abstract methods in concrete subclasses. NB in 2021 addGas should be something like add_energy...
-3

In your code snippet, you could also resolve this by providing an implementation for the __new__ method in the subclass, likewise:

def G(F):
    def __new__(cls):
        # do something here

But this is a hack and I advise you against it, unless you know what you are doing. For nearly all cases I advise you to use the abc module, that others before me have suggested.

Also when you create a new (base) class, make it subclass object, like this: class MyBaseClass(object):. I don't know if it is that much significant anymore, but it helps retain style consistency on your code

Comments