internals.doctest 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. .. Copyright (C) 2001-2019 NLTK Project
  2. .. For license information, see LICENSE.TXT
  3. ==========================================
  4. Unit tests for the nltk.utilities module
  5. ==========================================
  6. overridden()
  7. ~~~~~~~~~~~~
  8. >>> from nltk.internals import overridden
  9. The typical use case is in defining methods for an interface or
  10. abstract base class, in such a way that subclasses don't have to
  11. implement all of the methods:
  12. >>> class EaterI(object):
  13. ... '''Subclass must define eat() or batch_eat().'''
  14. ... def eat(self, food):
  15. ... if overridden(self.batch_eat):
  16. ... return self.batch_eat([food])[0]
  17. ... else:
  18. ... raise NotImplementedError()
  19. ... def batch_eat(self, foods):
  20. ... return [self.eat(food) for food in foods]
  21. As long as a subclass implements one method, it will be used to
  22. perform the other method:
  23. >>> class GoodEater1(EaterI):
  24. ... def eat(self, food):
  25. ... return 'yum'
  26. >>> GoodEater1().eat('steak')
  27. 'yum'
  28. >>> GoodEater1().batch_eat(['steak', 'peas'])
  29. ['yum', 'yum']
  30. >>> class GoodEater2(EaterI):
  31. ... def batch_eat(self, foods):
  32. ... return ['yum' for food in foods]
  33. >>> GoodEater2().eat('steak')
  34. 'yum'
  35. >>> GoodEater2().batch_eat(['steak', 'peas'])
  36. ['yum', 'yum']
  37. But if a subclass doesn't implement either one, then they'll get an
  38. error when they try to call them. (nb this is better than infinite
  39. recursion):
  40. >>> class BadEater1(EaterI):
  41. ... pass
  42. >>> BadEater1().eat('steak')
  43. Traceback (most recent call last):
  44. . . .
  45. NotImplementedError
  46. >>> BadEater1().batch_eat(['steak', 'peas'])
  47. Traceback (most recent call last):
  48. . . .
  49. NotImplementedError
  50. Trying to use the abstract base class itself will also result in an
  51. error:
  52. >>> class EaterI(EaterI):
  53. ... pass
  54. >>> EaterI().eat('steak')
  55. Traceback (most recent call last):
  56. . . .
  57. NotImplementedError
  58. >>> EaterI().batch_eat(['steak', 'peas'])
  59. Traceback (most recent call last):
  60. . . .
  61. NotImplementedError
  62. It's ok to use intermediate abstract classes:
  63. >>> class AbstractEater(EaterI):
  64. ... pass
  65. >>> class GoodEater3(AbstractEater):
  66. ... def eat(self, food):
  67. ... return 'yum'
  68. ...
  69. >>> GoodEater3().eat('steak')
  70. 'yum'
  71. >>> GoodEater3().batch_eat(['steak', 'peas'])
  72. ['yum', 'yum']
  73. >>> class GoodEater4(AbstractEater):
  74. ... def batch_eat(self, foods):
  75. ... return ['yum' for food in foods]
  76. >>> GoodEater4().eat('steak')
  77. 'yum'
  78. >>> GoodEater4().batch_eat(['steak', 'peas'])
  79. ['yum', 'yum']
  80. >>> class BadEater2(AbstractEater):
  81. ... pass
  82. >>> BadEater2().eat('steak')
  83. Traceback (most recent call last):
  84. . . .
  85. NotImplementedError
  86. >>> BadEater2().batch_eat(['steak', 'peas'])
  87. Traceback (most recent call last):
  88. . . .
  89. NotImplementedError
  90. Here's some extra tests:
  91. >>> class A(object):
  92. ... def f(x): pass
  93. >>> class B(A):
  94. ... def f(x): pass
  95. >>> class C(A): pass
  96. >>> class D(B): pass
  97. >>> overridden(A().f)
  98. False
  99. >>> overridden(B().f)
  100. True
  101. >>> overridden(C().f)
  102. False
  103. >>> overridden(D().f)
  104. True
  105. It works for classic classes, too:
  106. >>> class A:
  107. ... def f(x): pass
  108. >>> class B(A):
  109. ... def f(x): pass
  110. >>> class C(A): pass
  111. >>> class D(B): pass
  112. >>> overridden(A().f)
  113. False
  114. >>> overridden(B().f)
  115. True
  116. >>> overridden(C().f)
  117. False
  118. >>> overridden(D().f)
  119. True