|
|
- Metadata-Version: 2.1
- Name: simplegeneric
- Version: 0.8.1
- Summary: Simple generic functions (similar to Python's own len(), pickle.dump(), etc.)
- Home-page: http://cheeseshop.python.org/pypi/simplegeneric
- Author: Phillip J. Eby
- Author-email: peak@eby-sarna.com
- License: ZPL 2.1
- Platform: UNKNOWN
- Classifier: Development Status :: 6 - Mature
- Classifier: Development Status :: 7 - Inactive
- Classifier: Intended Audience :: Developers
- Classifier: License :: OSI Approved :: Zope Public License
- Classifier: Programming Language :: Python
- Classifier: Programming Language :: Python :: 2
- Classifier: Programming Language :: Python :: 2.4
- Classifier: Programming Language :: Python :: 2.5
- Classifier: Programming Language :: Python :: 2.6
- Classifier: Programming Language :: Python :: 2.7
- Classifier: Programming Language :: Python :: 3
- Classifier: Operating System :: OS Independent
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
-
- * New in 0.8: Source and tests are compatible with Python 3 (w/o ``setup.py``)
- * 0.8.1: setup.py is now compatible with Python 3 as well
- * New in 0.7: `Multiple Types or Objects`_
- * New in 0.6: `Inspection and Extension`_, and thread-safe method registration
-
- The ``simplegeneric`` module lets you define simple single-dispatch
- generic functions, akin to Python's built-in generic functions like
- ``len()``, ``iter()`` and so on. However, instead of using
- specially-named methods, these generic functions use simple lookup
- tables, akin to those used by e.g. ``pickle.dump()`` and other
- generic functions found in the Python standard library.
-
- As you can see from the above examples, generic functions are actually
- quite common in Python already, but there is no standard way to create
- simple ones. This library attempts to fill that gap, as generic
- functions are an `excellent alternative to the Visitor pattern`_, as
- well as being a great substitute for most common uses of adaptation.
-
- This library tries to be the simplest possible implementation of generic
- functions, and it therefore eschews the use of multiple or predicate
- dispatch, as well as avoiding speedup techniques such as C dispatching
- or code generation. But it has absolutely no dependencies, other than
- Python 2.4, and the implementation is just a single Python module of
- less than 100 lines.
-
-
- Usage
- -----
-
- Defining and using a generic function is straightforward::
-
- >>> from simplegeneric import generic
- >>> @generic
- ... def move(item, target):
- ... """Default implementation goes here"""
- ... print("what you say?!")
-
- >>> @move.when_type(int)
- ... def move_int(item, target):
- ... print("In AD %d, %s was beginning." % (item, target))
-
- >>> @move.when_type(str)
- ... def move_str(item, target):
- ... print("How are you %s!!" % item)
- ... print("All your %s are belong to us." % (target,))
-
- >>> zig = object()
- >>> @move.when_object(zig)
- ... def move_zig(item, target):
- ... print("You know what you %s." % (target,))
- ... print("For great justice!")
-
- >>> move(2101, "war")
- In AD 2101, war was beginning.
-
- >>> move("gentlemen", "base")
- How are you gentlemen!!
- All your base are belong to us.
-
- >>> move(zig, "doing")
- You know what you doing.
- For great justice!
-
- >>> move(27.0, 56.2)
- what you say?!
-
-
- Inheritance and Allowed Types
- -----------------------------
-
- Defining multiple methods for the same type or object is an error::
-
- >>> @move.when_type(str)
- ... def this_is_wrong(item, target):
- ... pass
- Traceback (most recent call last):
- ...
- TypeError: <function move...> already has method for type <...'str'>
-
- >>> @move.when_object(zig)
- ... def this_is_wrong(item, target): pass
- Traceback (most recent call last):
- ...
- TypeError: <function move...> already has method for object <object ...>
-
- And the ``when_type()`` decorator only accepts classes or types::
-
- >>> @move.when_type(23)
- ... def move_23(item, target):
- ... print("You have no chance to survive!")
- Traceback (most recent call last):
- ...
- TypeError: 23 is not a type or class
-
- Methods defined for supertypes are inherited following MRO order::
-
- >>> class MyString(str):
- ... """String subclass"""
-
- >>> move(MyString("ladies"), "drinks")
- How are you ladies!!
- All your drinks are belong to us.
-
- Classic class instances are also supported (although the lookup process
- is slower than for new-style instances)::
-
- >>> class X: pass
- >>> class Y(X): pass
-
- >>> @move.when_type(X)
- ... def move_x(item, target):
- ... print("Someone set us up the %s!!!" % (target,))
-
- >>> move(X(), "bomb")
- Someone set us up the bomb!!!
-
- >>> move(Y(), "dance")
- Someone set us up the dance!!!
-
-
- Multiple Types or Objects
- -------------------------
-
- As a convenience, you can now pass more than one type or object to the
- registration methods::
-
- >>> @generic
- ... def isbuiltin(ob):
- ... return False
- >>> @isbuiltin.when_type(int, str, float, complex, type)
- ... @isbuiltin.when_object(None, Ellipsis)
- ... def yes(ob):
- ... return True
-
- >>> isbuiltin(1)
- True
- >>> isbuiltin(object)
- True
- >>> isbuiltin(object())
- False
- >>> isbuiltin(X())
- False
- >>> isbuiltin(None)
- True
- >>> isbuiltin(Ellipsis)
- True
-
-
- Defaults and Docs
- -----------------
-
- You can obtain a function's default implementation using its ``default``
- attribute::
-
- >>> @move.when_type(Y)
- ... def move_y(item, target):
- ... print("Someone set us up the %s!!!" % (target,))
- ... move.default(item, target)
-
- >>> move(Y(), "dance")
- Someone set us up the dance!!!
- what you say?!
-
-
- ``help()`` and other documentation tools see generic functions as normal
- function objects, with the same name, attributes, docstring, and module as
- the prototype/default function::
-
- >>> help(move)
- Help on function move:
- ...
- move(*args, **kw)
- Default implementation goes here
- ...
-
-
- Inspection and Extension
- ------------------------
-
- You can find out if a generic function has a method for a type or object using
- the ``has_object()`` and ``has_type()`` methods::
-
- >>> move.has_object(zig)
- True
- >>> move.has_object(42)
- False
-
- >>> move.has_type(X)
- True
- >>> move.has_type(float)
- False
-
- Note that ``has_type()`` only queries whether there is a method registered for
- the *exact* type, not subtypes or supertypes::
-
- >>> class Z(X): pass
- >>> move.has_type(Z)
- False
-
- You can create a generic function that "inherits" from an existing generic
- function by calling ``generic()`` on the existing function::
-
- >>> move2 = generic(move)
- >>> move(2101, "war")
- In AD 2101, war was beginning.
-
- Any methods added to the new generic function override *all* methods in the
- "base" function::
-
- >>> @move2.when_type(X)
- ... def move2_X(item, target):
- ... print("You have no chance to survive make your %s!" % (target,))
-
- >>> move2(X(), "time")
- You have no chance to survive make your time!
-
- >>> move2(Y(), "time")
- You have no chance to survive make your time!
-
- Notice that even though ``move()`` has a method for type ``Y``, the method
- defined for ``X`` in ``move2()`` takes precedence. This is because the
- ``move`` function is used as the ``default`` method of ``move2``, and ``move2``
- has no method for type ``Y``::
-
- >>> move2.default is move
- True
- >>> move.has_type(Y)
- True
- >>> move2.has_type(Y)
- False
-
-
- Limitations
- -----------
-
- * The first argument is always used for dispatching, and it must always be
- passed *positionally* when the function is called.
-
- * Documentation tools don't see the function's original argument signature, so
- you have to describe it in the docstring.
-
- * If you have optional arguments, you must duplicate them on every method in
- order for them to work correctly. (On the plus side, it means you can have
- different defaults or required arguments for each method, although relying on
- that quirk probably isn't a good idea.)
-
- These restrictions may be lifted in later releases, if I feel the need. They
- would require runtime code generation the way I do it in ``RuleDispatch``,
- however, which is somewhat of a pain. (Alternately I could use the
- ``BytecodeAssembler`` package to do the code generation, as that's a lot easier
- to use than string-based code generation, but that would introduce more
- dependencies, and I'm trying to keep this simple so I can just
- toss it into Chandler without a big footprint increase.)
-
- .. _excellent alternative to the Visitor pattern: http://peak.telecommunity.com/DevCenter/VisitorRevisited
-
-
-
|