Speak and Shout

Tuesday, December 12, 2006

Abstract methods in Python

Following up one of my previous posts, I've been looking at code from Pygame Extended to get ideas for a Scene class to use with my Asteroids game. I'm requiring derived classes to override a process_events function in my Scene class, since each scene will need its own event loop.

In this case, it thought it would be good to have a way to specify that process_events was an abstract method. I guess I hadn't thought about abstract methods too much before in Python since I don't typically use inheritance, but I knew there wasn't anything like the notion of interfaces built into the language itself.

I started looking through the Python Cookbook for ideas and came across this article. Yikes! I wasn't sure I wanted to bother with metaclasses just for this feature. But in the comments I saw that someone else suggested raising an exception in the base class so that calling the method at runtime would fail unless there was an override.

I slapped my forehead after reading the Cookbook comment. Of course, in hindsight, it was obvious. But apparently it isn't obvious to other folks either. The pygext source code simply uses empty methods like the following:

class Foo(object):
   def update(self):
      pass # Override me in derived class


Of course, you can't distinguish pure virtual methods from regular virtual methods this way.

Even some very respectable folks have solved this problem differently: Peter Norvig, when writing his Python code for his Artificial Intelligence book, uses a made-up keyword called "abstract" which fails at runtime if it hasn't been overridden in a derived class. Not the most elegant solution either.

I really think handling abstract methods should be done at object creation like the previously mentioned Cookbook recipe does, but it should be built into Python itself. This may be one of the reasons people think OOP in Python is a hack ...

Labels: , , ,

3 Comments:

  • "raising an exception in the base class"

    There's a built-in exception for this purpose, of course:

    >>> help(NotImplementedError)
    Help on class NotImplementedError in module exceptions:

    class NotImplementedError(RuntimeError)
    | Method or function hasn't been implemented yet.
    |
    | Method resolution order:
    | NotImplementedError
    | RuntimeError
    | StandardError
    | Exception

    and also a placeholder value/type that can be used where you cannot use an exception:

    >>> help(NotImplemented)
    Help on NotImplementedType object:

    class NotImplementedType(object)

    By Blogger Fredrik, At 1:38 AM  

  • I only just noticed this entry, but I wanted to comment on the pygext reference. The methods that have the "override me in derived class" are not abstract methods per se. They are just methods whose default implementation does nothing. Implementing them in a derived class is not mandatory, that's why they don't raise an exception.

    By Blogger shang, At 4:39 AM  

  • Shang - sorry if I was confusing; I like pygext as a whole, but I find its "empty methods" to be uncommunicative for a user. There's really no distinction in that library between something you really should override to get any useful functionality vs. something that's just optional. That's why I was striving for something a little more robust in my own implementation.

    I'm seeing now that at least this issue is becoming more important and will probably get a language implementation of some sort.

    By Blogger Brandon Corfman, At 8:08 AM  

Post a Comment



<$I18N$LinksToThisPost>:

Create a Link

<< Home