You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

281 lines
8.5 KiB

4 years ago
  1. Metadata-Version: 2.1
  2. Name: simplegeneric
  3. Version: 0.8.1
  4. Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.)
  5. Home-page: http://cheeseshop.python.org/pypi/simplegeneric
  6. Author: Phillip J. Eby
  7. Author-email: peak@eby-sarna.com
  8. License: ZPL 2.1
  9. Platform: UNKNOWN
  10. Classifier: Development Status :: 6 - Mature
  11. Classifier: Development Status :: 7 - Inactive
  12. Classifier: Intended Audience :: Developers
  13. Classifier: License :: OSI Approved :: Zope Public License
  14. Classifier: Programming Language :: Python
  15. Classifier: Programming Language :: Python :: 2
  16. Classifier: Programming Language :: Python :: 2.4
  17. Classifier: Programming Language :: Python :: 2.5
  18. Classifier: Programming Language :: Python :: 2.6
  19. Classifier: Programming Language :: Python :: 2.7
  20. Classifier: Programming Language :: Python :: 3
  21. Classifier: Operating System :: OS Independent
  22. Classifier: Topic :: Software Development :: Libraries :: Python Modules
  23. * New in 0.8: Source and tests are compatible with Python 3 (w/o ``setup.py``)
  24. * 0.8.1: setup.py is now compatible with Python 3 as well
  25. * New in 0.7: `Multiple Types or Objects`_
  26. * New in 0.6: `Inspection and Extension`_, and thread-safe method registration
  27. The ``simplegeneric`` module lets you define simple single-dispatch
  28. generic functions, akin to Python's built-in generic functions like
  29. ``len()``, ``iter()`` and so on. However, instead of using
  30. specially-named methods, these generic functions use simple lookup
  31. tables, akin to those used by e.g. ``pickle.dump()`` and other
  32. generic functions found in the Python standard library.
  33. As you can see from the above examples, generic functions are actually
  34. quite common in Python already, but there is no standard way to create
  35. simple ones. This library attempts to fill that gap, as generic
  36. functions are an `excellent alternative to the Visitor pattern`_, as
  37. well as being a great substitute for most common uses of adaptation.
  38. This library tries to be the simplest possible implementation of generic
  39. functions, and it therefore eschews the use of multiple or predicate
  40. dispatch, as well as avoiding speedup techniques such as C dispatching
  41. or code generation. But it has absolutely no dependencies, other than
  42. Python 2.4, and the implementation is just a single Python module of
  43. less than 100 lines.
  44. Usage
  45. -----
  46. Defining and using a generic function is straightforward::
  47. >>> from simplegeneric import generic
  48. >>> @generic
  49. ... def move(item, target):
  50. ... """Default implementation goes here"""
  51. ... print("what you say?!")
  52. >>> @move.when_type(int)
  53. ... def move_int(item, target):
  54. ... print("In AD %d, %s was beginning." % (item, target))
  55. >>> @move.when_type(str)
  56. ... def move_str(item, target):
  57. ... print("How are you %s!!" % item)
  58. ... print("All your %s are belong to us." % (target,))
  59. >>> zig = object()
  60. >>> @move.when_object(zig)
  61. ... def move_zig(item, target):
  62. ... print("You know what you %s." % (target,))
  63. ... print("For great justice!")
  64. >>> move(2101, "war")
  65. In AD 2101, war was beginning.
  66. >>> move("gentlemen", "base")
  67. How are you gentlemen!!
  68. All your base are belong to us.
  69. >>> move(zig, "doing")
  70. You know what you doing.
  71. For great justice!
  72. >>> move(27.0, 56.2)
  73. what you say?!
  74. Inheritance and Allowed Types
  75. -----------------------------
  76. Defining multiple methods for the same type or object is an error::
  77. >>> @move.when_type(str)
  78. ... def this_is_wrong(item, target):
  79. ... pass
  80. Traceback (most recent call last):
  81. ...
  82. TypeError: <function move...> already has method for type <...'str'>
  83. >>> @move.when_object(zig)
  84. ... def this_is_wrong(item, target): pass
  85. Traceback (most recent call last):
  86. ...
  87. TypeError: <function move...> already has method for object <object ...>
  88. And the ``when_type()`` decorator only accepts classes or types::
  89. >>> @move.when_type(23)
  90. ... def move_23(item, target):
  91. ... print("You have no chance to survive!")
  92. Traceback (most recent call last):
  93. ...
  94. TypeError: 23 is not a type or class
  95. Methods defined for supertypes are inherited following MRO order::
  96. >>> class MyString(str):
  97. ... """String subclass"""
  98. >>> move(MyString("ladies"), "drinks")
  99. How are you ladies!!
  100. All your drinks are belong to us.
  101. Classic class instances are also supported (although the lookup process
  102. is slower than for new-style instances)::
  103. >>> class X: pass
  104. >>> class Y(X): pass
  105. >>> @move.when_type(X)
  106. ... def move_x(item, target):
  107. ... print("Someone set us up the %s!!!" % (target,))
  108. >>> move(X(), "bomb")
  109. Someone set us up the bomb!!!
  110. >>> move(Y(), "dance")
  111. Someone set us up the dance!!!
  112. Multiple Types or Objects
  113. -------------------------
  114. As a convenience, you can now pass more than one type or object to the
  115. registration methods::
  116. >>> @generic
  117. ... def isbuiltin(ob):
  118. ... return False
  119. >>> @isbuiltin.when_type(int, str, float, complex, type)
  120. ... @isbuiltin.when_object(None, Ellipsis)
  121. ... def yes(ob):
  122. ... return True
  123. >>> isbuiltin(1)
  124. True
  125. >>> isbuiltin(object)
  126. True
  127. >>> isbuiltin(object())
  128. False
  129. >>> isbuiltin(X())
  130. False
  131. >>> isbuiltin(None)
  132. True
  133. >>> isbuiltin(Ellipsis)
  134. True
  135. Defaults and Docs
  136. -----------------
  137. You can obtain a function's default implementation using its ``default``
  138. attribute::
  139. >>> @move.when_type(Y)
  140. ... def move_y(item, target):
  141. ... print("Someone set us up the %s!!!" % (target,))
  142. ... move.default(item, target)
  143. >>> move(Y(), "dance")
  144. Someone set us up the dance!!!
  145. what you say?!
  146. ``help()`` and other documentation tools see generic functions as normal
  147. function objects, with the same name, attributes, docstring, and module as
  148. the prototype/default function::
  149. >>> help(move)
  150. Help on function move:
  151. ...
  152. move(*args, **kw)
  153. Default implementation goes here
  154. ...
  155. Inspection and Extension
  156. ------------------------
  157. You can find out if a generic function has a method for a type or object using
  158. the ``has_object()`` and ``has_type()`` methods::
  159. >>> move.has_object(zig)
  160. True
  161. >>> move.has_object(42)
  162. False
  163. >>> move.has_type(X)
  164. True
  165. >>> move.has_type(float)
  166. False
  167. Note that ``has_type()`` only queries whether there is a method registered for
  168. the *exact* type, not subtypes or supertypes::
  169. >>> class Z(X): pass
  170. >>> move.has_type(Z)
  171. False
  172. You can create a generic function that "inherits" from an existing generic
  173. function by calling ``generic()`` on the existing function::
  174. >>> move2 = generic(move)
  175. >>> move(2101, "war")
  176. In AD 2101, war was beginning.
  177. Any methods added to the new generic function override *all* methods in the
  178. "base" function::
  179. >>> @move2.when_type(X)
  180. ... def move2_X(item, target):
  181. ... print("You have no chance to survive make your %s!" % (target,))
  182. >>> move2(X(), "time")
  183. You have no chance to survive make your time!
  184. >>> move2(Y(), "time")
  185. You have no chance to survive make your time!
  186. Notice that even though ``move()`` has a method for type ``Y``, the method
  187. defined for ``X`` in ``move2()`` takes precedence. This is because the
  188. ``move`` function is used as the ``default`` method of ``move2``, and ``move2``
  189. has no method for type ``Y``::
  190. >>> move2.default is move
  191. True
  192. >>> move.has_type(Y)
  193. True
  194. >>> move2.has_type(Y)
  195. False
  196. Limitations
  197. -----------
  198. * The first argument is always used for dispatching, and it must always be
  199. passed *positionally* when the function is called.
  200. * Documentation tools don't see the function's original argument signature, so
  201. you have to describe it in the docstring.
  202. * If you have optional arguments, you must duplicate them on every method in
  203. order for them to work correctly. (On the plus side, it means you can have
  204. different defaults or required arguments for each method, although relying on
  205. that quirk probably isn't a good idea.)
  206. These restrictions may be lifted in later releases, if I feel the need. They
  207. would require runtime code generation the way I do it in ``RuleDispatch``,
  208. however, which is somewhat of a pain. (Alternately I could use the
  209. ``BytecodeAssembler`` package to do the code generation, as that's a lot easier
  210. to use than string-based code generation, but that would introduce more
  211. dependencies, and I'm trying to keep this simple so I can just
  212. toss it into Chandler without a big footprint increase.)
  213. .. _excellent alternative to the Visitor pattern: http://peak.telecommunity.com/DevCenter/VisitorRevisited