Unit tests for the nltk.utilities module

overridden()

>>> from nltk.internals import overridden

The typical use case is in defining methods for an interface or abstract base class, in such a way that subclasses don't have to implement all of the methods:

>>> class EaterI(object):
...     '''Subclass must define eat() or batch_eat().'''
...     def eat(self, food):
...         if overridden(self.batch_eat):
...             return self.batch_eat([food])[0]
...         else:
...             raise NotImplementedError()
...     def batch_eat(self, foods):
...         return [self.eat(food) for food in foods]

As long as a subclass implements one method, it will be used to perform the other method:

>>> class GoodEater1(EaterI):
...     def eat(self, food):
...         return 'yum'
>>> GoodEater1().eat('steak')
'yum'
>>> GoodEater1().batch_eat(['steak', 'peas'])
['yum', 'yum']
>>> class GoodEater2(EaterI):
...     def batch_eat(self, foods):
...         return ['yum' for food in foods]
>>> GoodEater2().eat('steak')
'yum'
>>> GoodEater2().batch_eat(['steak', 'peas'])
['yum', 'yum']

But if a subclass doesn't implement either one, then they'll get an error when they try to call them. (nb this is better than infinite recursion):

>>> class BadEater1(EaterI):
...     pass
>>> BadEater1().eat('steak')
Traceback (most recent call last):
  . . .
NotImplementedError
>>> BadEater1().batch_eat(['steak', 'peas'])
Traceback (most recent call last):
  . . .
NotImplementedError

Trying to use the abstract base class itself will also result in an error:

>>> class EaterI(EaterI):
...     pass
>>> EaterI().eat('steak')
Traceback (most recent call last):
  . . .
NotImplementedError
>>> EaterI().batch_eat(['steak', 'peas'])
Traceback (most recent call last):
  . . .
NotImplementedError

It's ok to use intermediate abstract classes:

>>> class AbstractEater(EaterI):
...     pass
>>> class GoodEater3(AbstractEater):
...     def eat(self, food):
...         return 'yum'
...
>>> GoodEater3().eat('steak')
'yum'
>>> GoodEater3().batch_eat(['steak', 'peas'])
['yum', 'yum']
>>> class GoodEater4(AbstractEater):
...     def batch_eat(self, foods):
...         return ['yum' for food in foods]
>>> GoodEater4().eat('steak')
'yum'
>>> GoodEater4().batch_eat(['steak', 'peas'])
['yum', 'yum']
>>> class BadEater2(AbstractEater):
...     pass
>>> BadEater2().eat('steak')
Traceback (most recent call last):
  . . .
NotImplementedError
>>> BadEater2().batch_eat(['steak', 'peas'])
Traceback (most recent call last):
  . . .
NotImplementedError

Here's some extra tests:

>>> class A(object):
...     def f(x): pass
>>> class B(A):
...     def f(x): pass
>>> class C(A): pass
>>> class D(B): pass
>>> overridden(A().f)
False
>>> overridden(B().f)
True
>>> overridden(C().f)
False
>>> overridden(D().f)
True

It works for classic classes, too:

>>> class A:
...     def f(x): pass
>>> class B(A):
...     def f(x): pass
>>> class C(A): pass
>>> class D(B): pass
>>> overridden(A().f)
False
>>> overridden(B().f)
True
>>> overridden(C().f)
False
>>> overridden(D().f)
True