python @abstractmethod decorator
Asked Answered
A

2

111

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.

Anaplasty answered 25/8, 2011 at 19:53 Comment(2)
Which Python? It reports the error just fine for me. Also, you can always raise NotImplementedError instead of using abc.Hoskinson
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.Anaplasty
O
122

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

Occidentalize answered 25/8, 2011 at 20:2 Comment(5)
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...Anaplasty
@Anaplasty : python3 was designed with less backward compatibility with it's precedent python2 (python.org/dev/peps/pep-3000/#compatibility-and-transition) but to solve backward incompatibilities between the two version you should use 2to3.py script (docs.python.org/library/2to3.html).Occidentalize
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.Anaplasty
Instead of 2to3 you can also use six: python-future.org/compatible_idioms.html#metaclassesKnavery
If you want to understand more conceptually. Please refer this video by Raymond Hettinger. youtube.com/watch?v=S_ipdVNSFloAurore
B
26

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

Bennir answered 27/4, 2021 at 13:55 Comment(2)
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 @Occidentalize 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.Pokelogan
It's the same. ABC declares it also like so class ABC(metaclass=ABCMeta):Malherbe

© 2022 - 2024 — McMap. All rights reserved.