100

I have read python docs about abstract base classes:

From here:

abc.abstractmethod(function) A decorator indicating abstract methods.

Using this decorator requires that the class’s metaclass is ABCMeta or is derived from it. A class that has a metaclass derived from ABCMeta cannot be instantiated unless all of its abstract methods and properties are overridden.

And here

You can apply the @abstractmethod decorator to methods such as draw() that must be implemented; Python will then raise an exception for classes that don’t define the method. Note that the exception is only raised when you actually try to create an instance of a subclass lacking the method.

I've used this code to test that out:

import abc

class AbstractClass(object):
  __metaclass__ = abc.ABCMeta

  @abc.abstractmethod
  def abstractMethod(self):
    return

class ConcreteClass(AbstractClass):
  def __init__(self):
    self.me = "me"

c = ConcreteClass()
c.abstractMethod()

The code goes fine, so I don't get it. If I type c.abstractMethod I get:

<bound method ConcreteClass.abstractMethod of <__main__.ConcreteClass object at 0x7f694da1c3d0>>

What I'm missing here? ConcreteClass must implement the abstract methods, but I get no exception.

Richard Hansen
  • 51,690
  • 20
  • 90
  • 97
Sebastian
  • 4,770
  • 4
  • 42
  • 43
  • 3
    Which Python? It reports the error just fine for me. Also, you can always raise NotImplementedError instead of using `abc`. – Cat Plus Plus Aug 25 '11 at 19:58
  • I post a comment on mouad answer, the link from `python` was set as default to `python3`. I'll keep in mind raising the exception, as writting portable code with that changes on python seems far away from my python knowledge. – Sebastian Aug 25 '11 at 20:12

2 Answers2

117

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

Leif Jones
  • 320
  • 6
  • 21
mouad
  • 67,571
  • 18
  • 114
  • 106
  • Thanks, that seems a huge change between versions (my distro was pointing `python` to default as `python3`). How to write that for backward compatibility? I guess this could be another question... – Sebastian Aug 25 '11 at 20:10
  • 3
    @Sebastian : `python3` was designed with less backward compatibility with it's precedent python2 (http://www.python.org/dev/peps/pep-3000/#compatibility-and-transition) but to solve backward incompatibilities between the two version you should use 2to3.py script (http://docs.python.org/library/2to3.html). – mouad Aug 25 '11 at 20:16
  • nice, I had test it out. I can continue using the `__metaclass__` syntax and use that script to convert to `python3` syntax. It just did what you stated in the example. Thanks. – Sebastian Aug 25 '11 at 20:23
  • Thanks for the answer. I was searching for a long time trying to work out why my ABC wasn't working as expected and you saved me. – Mike Chamberlain Feb 11 '12 at 13:32
  • 1
    Instead of 2to3 you can also use six: http://python-future.org/compatible_idioms.html#metaclasses – de1 Oct 27 '17 at 22:40
  • 2
    If you want to understand more conceptually. Please refer this video by Raymond Hettinger. https://www.youtube.com/watch?v=S_ipdVNSFlo – Akarsh Jain Nov 30 '19 at 15:07
23

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

l001d
  • 723
  • 9
  • 15
  • I have run your solution and it works like expected, giving `TypeError: Can't instantiate abstract class ConcreteClass with abstract method abstractMethod`. Now, compared to the answer by @mouad there is no usage of `metaclass=abc.ABCMeta` in the base classes' definition. Is it really the same, do you know? If yes, then I like your solution a little more, because of less text, but maybe this is just a matter of taste. – Pascal Jan 20 '23 at 07:46