<br><br><div><span class="gmail_quote">On 4/30/07, <b class="gmail_sendername">Phillip J. Eby</b> &lt;<a href="mailto:pje@telecommunity.com">pje@telecommunity.com</a>&gt; wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
This is just the first draft (also checked into SVN), and doesn&#39;t include<br>the details of how the extension API works (so that third-party interfaces<br>and generic functions can interoperate using the same decorators,
<br>annotations, etc.).<br><br>Comments and questions appreciated, as it&#39;ll help drive better explanations<br>of both the design and rationales.&nbsp;&nbsp;I&#39;m usually not that good at guessing<br>what other people will want to know (or are likely to misunderstand) until
<br>I get actual questions.<br><br><br>PEP: 3124<br>Title: Overloading, Generic Functions, Interfaces, and Adaptation<br>Version: $Revision: 55029 $<br>Last-Modified: $Date: 2007-04-30 18:48:06 -0400 (Mon, 30 Apr 2007) $<br>
Author: Phillip J. Eby &lt;<a href="mailto:pje@telecommunity.com">pje@telecommunity.com</a>&gt;<br>Discussions-To: Python 3000 List &lt;<a href="mailto:python-3000@python.org">python-3000@python.org</a>&gt;<br>Status: Draft
<br>Type: Standards Track<br>Requires: 3107, 3115, 3119<br>Replaces: 245, 246<br>Content-Type: text/x-rst<br>Created: 28-Apr-2007<br>Post-History: 30-Apr-2007</blockquote><div><br>[snip]<br>&nbsp;</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<br>&quot;Before&quot; and &quot;After&quot; Methods<br>~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br><br>In addition to the simple next-method chaining shown above, it is<br>sometimes useful to have other ways of combining methods.&nbsp;&nbsp;For
<br>example, the &quot;observer pattern&quot; can sometimes be implemented by adding<br>extra methods to a function, that execute before or after the normal<br>implementation.<br><br>To support these use cases, the ``overloading`` module will supply
<br>``@before``, ``@after``, and ``@around`` decorators, that roughly<br>correspond to the same types of methods in the Common Lisp Object<br>System (CLOS), or the corresponding &quot;advice&quot; types in AspectJ.<br><br>
Like ``@when``, all of these decorators must be passed the function to<br>be overloaded, and can optionally accept a predicate as well::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; def begin_transaction(db):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print &quot;Beginning the actual transaction&quot;
<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp; @before(begin_transaction)<br>&nbsp;&nbsp;&nbsp;&nbsp; def check_single_access(db: SingletonDB):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if db.inuse:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; raise TransactionError(&quot;Database already in use&quot;)<br><br>&nbsp;&nbsp;&nbsp;&nbsp; @after(begin_transaction)
<br>&nbsp;&nbsp;&nbsp;&nbsp; def start_logging(db: LoggableDB):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; db.set_log_level(VERBOSE)</blockquote><div><br><br>If we are looking at doing Design By Contract using @before and @after (preconditions and postconditions), shouldn&#39;t there be some way of getting at the return value in functions decorated with @after?&nbsp; For example, it seems reasonable to require an extra argument, perhaps at the beginning:
<br><br>def successor(num):<br>&nbsp; return num + 1<br><br>@before(successor)<br>def check_positive(num: int):<br>&nbsp; if num &lt; 0:<br>&nbsp;&nbsp;&nbsp; raise PreconditionError(&quot;Positive integer inputs required&quot;)<br><br>@after(successor)
<br>def check_successor(returned, num:int):<br>&nbsp; if returned != num + 1:<br>&nbsp;&nbsp;&nbsp; raise PostconditionError(&quot;successor failed to do its job&quot;)<br><br>Or am I missing something about how @after works?<br><br>+1, BTW, on this whole idea.
<br><br>- C<br>&nbsp;</div><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">``@before`` and ``@after`` methods are invoked either before or after
<br>the main function body, and are *never considered ambiguous*.&nbsp;&nbsp;That<br>is, it will not cause any errors to have multiple &quot;before&quot; or &quot;after&quot;<br>methods with identical or overlapping signatures.&nbsp;&nbsp;Ambiguities are
<br>resolved using the order in which the methods were added to the<br>target function.<br><br>&quot;Before&quot; methods are invoked most-specific method first, with<br>ambiguous methods being executed in the order they were added.&nbsp;&nbsp;All
<br>&quot;before&quot; methods are called before any of the function&#39;s &quot;primary&quot;<br>methods (i.e. normal ``@overload`` methods) are executed.<br><br>&quot;After&quot; methods are invoked in the *reverse* order, after all of the
<br>function&#39;s &quot;primary&quot; methods are executed.&nbsp;&nbsp;That is, they are executed<br>least-specific methods first, with ambiguous methods being executed in<br>the reverse of the order in which they were added.<br><br>
The return values of both &quot;before&quot; and &quot;after&quot; methods are ignored,<br>and any uncaught exceptions raised by *any* methods (primary or other)<br>immediately end the dispatching process.&nbsp;&nbsp;&quot;Before&quot; and &quot;after&quot; methods
<br>cannot have ``__proceed__`` arguments, as they are not responsible<br>for calling any other methods.&nbsp;&nbsp;They are simply called as a<br>notification before or after the primary methods.<br><br>Thus, &quot;before&quot; and &quot;after&quot; methods can be used to check or establish
<br>preconditions (e.g. by raising an error if the conditions aren&#39;t met)<br>or to ensure postconditions, without needing to duplicate any existing<br>functionality.<br><br><br>&quot;Around&quot; Methods<br>~~~~~~~~~~~~~~~~
<br><br>The ``@around`` decorator declares a method as an &quot;around&quot; method.<br>&quot;Around&quot; methods are much like primary methods, except that the<br>least-specific &quot;around&quot; method has higher precedence than the
<br>most-specific &quot;before&quot; or method.<br><br>Unlike &quot;before&quot; and &quot;after&quot; methods, however, &quot;Around&quot; methods *are*<br>responsible for calling their ``__proceed__`` argument, in order to
<br>continue the invocation process.&nbsp;&nbsp;&quot;Around&quot; methods are usually used<br>to transform input arguments or return values, or to wrap specific<br>cases with special error handling or try/finally conditions, e.g.::
<br><br>&nbsp;&nbsp;&nbsp;&nbsp; @around(commit_transaction)<br>&nbsp;&nbsp;&nbsp;&nbsp; def lock_while_committing(__proceed__, db: SingletonDB):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; with db.global_lock:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return __proceed__(db)<br><br>They can also be used to replace the normal handling for a specific
<br>case, by *not* invoking the ``__proceed__`` function.<br><br>The ``__proceed__`` given to an &quot;around&quot; method will either be the<br>next applicable &quot;around&quot; method, a ``DispatchError`` instance,<br>
or a synthetic method object that will call all the &quot;before&quot; methods,<br>followed by the primary method chain, followed by all the &quot;after&quot;<br>methods, and return the result from the primary method chain.
<br><br>Thus, just as with normal methods, ``__proceed__`` can be checked for<br>``DispatchError``-ness, or simply invoked.&nbsp;&nbsp;The &quot;around&quot; method should<br>return the value returned by ``__proceed__``, unless of course it
<br>wishes to modify or replace it with a different return value for the<br>function as a whole.<br><br><br>Custom Combinations<br>~~~~~~~~~~~~~~~~~~~<br><br>The decorators described above (``@overload``, ``@when``, ``@before``,
<br>``@after``, and ``@around``) collectively implement what in CLOS is<br>called the &quot;standard method combination&quot; -- the most common patterns<br>used in combining methods.<br><br>Sometimes, however, an application or library may have use for a more
<br>sophisticated type of method combination.&nbsp;&nbsp;For example, if you<br>would like to have &quot;discount&quot; methods that return a percentage off,<br>to be subtracted from the value returned by the primary method(s),<br>
you might write something like this::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; from overloading import always_overrides, merge_by_default<br>&nbsp;&nbsp;&nbsp;&nbsp; from overloading import Around, Before, After, Method, MethodList<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class Discount(MethodList):
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Apply return values as discounts&quot;&quot;&quot;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def __call__(self, *args, **kw):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval = self.tail(*args, **kw)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for sig, body in self.sorted
():<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; retval -= retval * body(*args, **kw)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return retval<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # merge discounts by priority<br>&nbsp;&nbsp;&nbsp;&nbsp; merge_by_default(Discount)<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # discounts have precedence over before/after/primary methods
<br>&nbsp;&nbsp;&nbsp;&nbsp; always_overrides(Discount, Before)<br>&nbsp;&nbsp;&nbsp;&nbsp; always_overrides(Discount, After)<br>&nbsp;&nbsp;&nbsp;&nbsp; always_overrides(Discount, Method)<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # but not over &quot;around&quot; methods<br>&nbsp;&nbsp;&nbsp;&nbsp; always_overrides(Around, Discount)
<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # Make a decorator called &quot;discount&quot; that works just like the<br>&nbsp;&nbsp;&nbsp;&nbsp; # standard decorators...<br>&nbsp;&nbsp;&nbsp;&nbsp; discount = Discount.make_decorator(&#39;discount&#39;)<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # and now let&#39;s use it...
<br>&nbsp;&nbsp;&nbsp;&nbsp; def price(product):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return product.list_price<br><br>&nbsp;&nbsp;&nbsp;&nbsp; @discount(price)<br>&nbsp;&nbsp;&nbsp;&nbsp; def ten_percent_off_shoes(product: Shoe)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return Decimal(&#39;0.1&#39;)<br><br>Similar techniques can be used to implement a wide variety of
<br>CLOS-style method qualifiers and combination rules.&nbsp;&nbsp;The process of<br>creating custom method combination objects and their corresponding<br>decorators is described in more detail under the `Extension API`_<br>section.
<br><br>Note, by the way, that the ``@discount`` decorator shown will work<br>correctly with any new predicates defined by other code.&nbsp;&nbsp;For example,<br>if ``zope.interface`` were to register its interface types to work<br>
correctly as argument annotations, you would be able to specify<br>discounts on the basis of its interface types, not just classes or<br>``overloading``-defined interface types.<br><br>Similarly, if a library like RuleDispatch or PEAK-Rules were to
<br>register an appropriate predicate implementation and dispatch engine,<br>one would then be able to use those predicates for discounts as well,<br>e.g.::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; from somewhere import Pred&nbsp;&nbsp;# some predicate implementation
<br><br>&nbsp;&nbsp;&nbsp;&nbsp; @discount(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; price,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Pred(&quot;isinstance(product,Shoe) and&quot;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot; product.material.name==&#39;Blue Suede&#39;&quot;)<br>&nbsp;&nbsp;&nbsp;&nbsp; )<br>&nbsp;&nbsp;&nbsp;&nbsp; def forty_off_blue_suede_shoes(product):
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return Decimal(&#39;0.4&#39;)<br><br>The process of defining custom predicate types and dispatching engines<br>is also described in more detail under the `Extension API`_ section.<br><br><br>Overloading Inside Classes
<br>--------------------------<br><br>All of the decorators above have a special additional behavior when<br>they are directly invoked within a class body: the first parameter<br>(other than ``__proceed__``, if present) of the decorated function
<br>will be treated as though it had an annotation equal to the class<br>in which it was defined.<br><br>That is, this code::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class And(object):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # ...<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @when(get_conjuncts)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def __conjuncts(self):
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.conjuncts<br><br>produces the same effect as this (apart from the existence of a<br>private method)::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class And(object):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # ...<br><br>&nbsp;&nbsp;&nbsp;&nbsp; @when(get_conjuncts)<br>&nbsp;&nbsp;&nbsp;&nbsp; def get_conjuncts_of_and(ob: And):
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return ob.conjuncts<br><br>This behavior is both a convenience enhancement when defining lots of<br>methods, and a requirement for safely distinguishing multi-argument<br>overloads in subclasses.&nbsp;&nbsp;Consider, for example, the following code::
<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class A(object):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def foo(self, ob):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print &quot;got an object&quot;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @overload<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def foo(__proceed__, self, ob:Iterable):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print &quot;it&#39;s iterable!&quot;
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return __proceed__(self, ob)<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp; class B(A):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foo = A.foo&nbsp;&nbsp;&nbsp;&nbsp; # foo must be defined in local namespace<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @overload<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def foo(__proceed__, self, ob:Iterable):
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; print &quot;B got an iterable!&quot;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return __proceed__(self, ob)<br><br>Due to the implicit class rule, calling ``B().foo([])`` will print<br>&quot;B got an iterable!&quot; followed by &quot;it&#39;s iterable!&quot;, and finally,
<br>&quot;got an object&quot;, while ``A().foo([])`` would print only the messages<br>defined in ``A``.<br><br>Conversely, without the implicit class rule, the two &quot;Iterable&quot;<br>methods would have the exact same applicability conditions, so calling
<br>either ``A().foo([])`` or ``B().foo([])`` would result in an<br>``AmbiguousMethods`` error.<br><br>It is currently an open issue to determine the best way to implement<br>this rule in Python 3.0.&nbsp;&nbsp;Under Python 2.x, a class&#39; metaclass was
<br>not chosen until the end of the class body, which means that<br>decorators could insert a custom metaclass to do processing of this<br>sort.&nbsp;&nbsp;(This is how RuleDispatch, for example, implements the implicit<br>class rule.)
<br><br>PEP 3115, however, requires that a class&#39; metaclass be determined<br>*before* the class body has executed, making it impossible to use this<br>technique for class decoration any more.<br><br>At this writing, discussion on this issue is ongoing.
<br><br><br>Interfaces and Adaptation<br>-------------------------<br><br>The ``overloading`` module provides a simple implementation of<br>interfaces and adaptation.&nbsp;&nbsp;The following example defines an<br>``IStack`` interface, and declares that ``list`` objects support it::
<br><br>&nbsp;&nbsp;&nbsp;&nbsp; from overloading import abstract, Interface<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class IStack(Interface):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @abstract<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def push(self, ob)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Push &#39;ob&#39; onto the stack&quot;&quot;&quot;
<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @abstract<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def pop(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Pop a value and return it&quot;&quot;&quot;<br><br><br>&nbsp;&nbsp;&nbsp;&nbsp; when(IStack.push, (list, object))(list.append)<br>&nbsp;&nbsp;&nbsp;&nbsp; when(IStack.pop, (list,))(
list.pop)<br><br>&nbsp;&nbsp;&nbsp;&nbsp; mylist = []<br>&nbsp;&nbsp;&nbsp;&nbsp; mystack = IStack(mylist)<br>&nbsp;&nbsp;&nbsp;&nbsp; mystack.push(42)<br>&nbsp;&nbsp;&nbsp;&nbsp; assert mystack.pop()==42<br><br>The ``Interface`` class is a kind of &quot;universal adapter&quot;.&nbsp;&nbsp;It accepts<br>a single argument: an object to adapt.&nbsp;&nbsp;It then binds all its methods
<br>to the target object, in place of itself.&nbsp;&nbsp;Thus, calling<br>``mystack.push(42``) is the same as calling<br>``IStack.push(mylist, 42)``.<br><br>The ``@abstract`` decorator marks a function as being abstract: i.e.,<br>having no implementation.&nbsp;&nbsp;If an ``@abstract`` function is called,
<br>it raises ``NoApplicableMethods``.&nbsp;&nbsp;To become executable, overloaded<br>methods must be added using the techniques previously described. (That<br>is, methods can be added using ``@when``, ``@before``, ``@after``,<br>``@around``, or any custom method combination decorators.)
<br><br>In the example above, the ``list.append`` method is added as a method<br>for ``IStack.push()`` when its arguments are a list and an arbitrary<br>object.&nbsp;&nbsp;Thus, ``IStack.push(mylist, 42)`` is translated to<br>``list.append(mylist, 42)``, thereby implementing the desired
<br>operation.<br><br>(Note: the ``@abstract`` decorator is not limited to use in interface<br>definitions; it can be used anywhere that you wish to create an<br>&quot;empty&quot; generic function that initially has no methods.&nbsp;&nbsp;In
<br>particular, it need not be used inside a class.)<br><br><br>Subclassing and Re-assembly<br>~~~~~~~~~~~~~~~~~~~~~~~~~~~<br><br>Interfaces can be subclassed::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class ISizedStack(IStack):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @abstract
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def __len__(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Return the number of items on the stack&quot;&quot;&quot;<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # define __len__ support for ISizedStack<br>&nbsp;&nbsp;&nbsp;&nbsp; when(ISizedStack.__len__, (list,))(list.__len__)
<br><br>Or assembled by combining functions from existing interfaces::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class Sizable(Interface):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __len__ = ISizedStack.__len__<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # list now implements Sizable as well as ISizedStack, without
<br>&nbsp;&nbsp;&nbsp;&nbsp; # making any new declarations!<br><br>A class can be considered to &quot;adapt to&quot; an interface at a given<br>point in time, if no method defined in the interface is guaranteed to<br>raise a ``NoApplicableMethods`` error if invoked on an instance of
<br>that class at that point in time.<br><br>In normal usage, however, it is &quot;easier to ask forgiveness than<br>permission&quot;.&nbsp;&nbsp;That is, it is easier to simply use an interface on<br>an object by adapting it to the interface (
e.g. ``IStack(mylist)``)<br>or invoking interface methods directly (e.g. ``IStack.push(mylist,<br>42)``), than to try to figure out whether the object is adaptable to<br>(or directly implements) the interface.<br><br><br>
Implementing an Interface in a Class<br>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br><br>It is possible to declare that a class directly implements an<br>interface, using the ``declare_implementation()`` function::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; from overloading import declare_implementation
<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class Stack(object):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def __init__(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.data = []<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def push(self, ob):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.data.append(ob)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def pop(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self.data.pop
()<br><br>&nbsp;&nbsp;&nbsp;&nbsp; declare_implementation(IStack, Stack)<br><br>The ``declare_implementation()`` call above is roughly equivalent to<br>the following steps::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; when(IStack.push, (Stack,object))(lambda self, ob: self.push
(ob))<br>&nbsp;&nbsp;&nbsp;&nbsp; when(IStack.pop, (Stack,))(lambda self, ob: self.pop())<br><br>That is, calling ``IStack.push()`` or ``IStack.pop()`` on an instance<br>of any subclass of ``Stack``, will simply delegate to the actual<br>``push()`` or ``pop()`` methods thereof.
<br><br>For the sake of efficiency, calling ``IStack(s)`` where ``s`` is an<br>instance of ``Stack``, **may** return ``s`` rather than an ``IStack``<br>adapter.&nbsp;&nbsp;(Note that calling ``IStack(x)`` where ``x`` is already an<br>
``IStack`` adapter will always return ``x`` unchanged; this is an<br>additional optimization allowed in cases where the adaptee is known<br>to *directly* implement the interface, without adaptation.)<br><br>For convenience, it may be useful to declare implementations in the
<br>class header, e.g.::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class Stack(metaclass=Implementer, implements=IStack):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...<br><br>Instead of calling ``declare_implementation()`` after the end of the<br>suite.<br><br><br>Interfaces as Type Specifiers
<br>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br><br>``Interface`` subclasses can be used as argument annotations to<br>indicate what type of objects are acceptable to an overload, e.g.::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; @overload<br>&nbsp;&nbsp;&nbsp;&nbsp; def traverse(g: IGraph, s: IStack):
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; g = IGraph(g)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = IStack(s)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # etc....<br><br>Note, however, that the actual arguments are *not* changed or adapted<br>in any way by the mere use of an interface as a type specifier.&nbsp;&nbsp;You
<br>must explicitly cast the objects to the appropriate interface, as<br>shown above.<br><br>Note, however, that other patterns of interface use are possible.<br>For example, other interface implementations might not support
<br>adaptation, or might require that function arguments already be<br>adapted to the specified interface.&nbsp;&nbsp;So the exact semantics of using<br>an interface as a type specifier are dependent on the interface<br>objects you actually use.
<br><br>For the interface objects defined by this PEP, however, the semantics<br>are as described above.&nbsp;&nbsp;An interface I1 is considered &quot;more specific&quot;<br>than another interface I2, if the set of descriptors in I1&#39;s
<br>inheritance hierarchy are a proper superset of the descriptors in I2&#39;s<br>inheritance hierarchy.<br><br>So, for example, ``ISizedStack`` is more specific than both<br>``ISizable`` and ``ISizedStack``, irrespective of the inheritance
<br>relationships between these interfaces.&nbsp;&nbsp;It is purely a question of<br>what operations are included within those interfaces -- and the<br>*names* of the operations are unimportant.<br><br>Interfaces (at least the ones provided by ``overloading``) are always
<br>considered less-specific than concrete classes.&nbsp;&nbsp;Other interface<br>implementations can decide on their own specificity rules, both<br>between interfaces and other interfaces, and between interfaces and<br>classes.<br>
<br><br>Non-Method Attributes in Interfaces<br>~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~<br><br>The ``Interface`` implementation actually treats all attributes and<br>methods (i.e. descriptors) in the same way: their ``__get__`` (and
<br>``__set__`` and ``__delete__``, if present) methods are called with<br>the wrapped (adapted) object as &quot;self&quot;.&nbsp;&nbsp;For functions, this has the<br>effect of creating a bound method linking the generic function to the
<br>wrapped object.<br><br>For non-function attributes, it may be easiest to specify them using<br>the ``property`` built-in, and the corresponding ``fget``, ``fset``,<br>and ``fdel`` attributes::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class ILength(Interface):
<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @property<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @abstract<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def length(self):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;&quot;&quot;Read-only length attribute&quot;&quot;&quot;<br><br>&nbsp;&nbsp;&nbsp;&nbsp; # ILength(aList).length == list.__len__(aList)<br>&nbsp;&nbsp;&nbsp;&nbsp; when(
ILength.length.fget, (list,))(list.__len__)<br><br>Alternatively, methods such as ``_get_foo()`` and ``_set_foo()``<br>may be defined as part of the interface, and the property defined<br>in terms of those methods, but this a bit more difficult for users
<br>to implement correctly when creating a class that directly implements<br>the interface, as they would then need to match all the individual<br>method names, not just the name of the property or attribute.<br><br><br>Aspects
<br>-------<br><br>The adaptation system provided assumes that adapters are &quot;stateless&quot;,<br>which is to say that adapters have no attributes or storage apart from<br>those of the adapted object.&nbsp;&nbsp;This follows the &quot;typeclass/instance&quot;
<br>model of Haskell, and the concept of &quot;pure&quot; (i.e., transitively<br>composable) adapters.<br><br>However, there are occasionally cases where, to provide a complete<br>implementation of some interface, some sort of additional state is
<br>required.<br><br>One possibility of course, would be to attach monkeypatched &quot;private&quot;<br>attributes to the adaptee.&nbsp;&nbsp;But this is subject to name collisions,<br>and complicates the process of initialization.&nbsp;&nbsp;It also doesn&#39;t work
<br>on objects that don&#39;t have a ``__dict__`` attribute.<br><br>So the ``Aspect`` class is provided to make it easy to attach extra<br>information to objects that either:<br><br>1. have a ``__dict__`` attribute (so aspect instances can be stored
<br>&nbsp;&nbsp;&nbsp;&nbsp;in it, keyed by aspect class),<br><br>2. support weak referencing (so aspect instances can be managed using<br>&nbsp;&nbsp;&nbsp;&nbsp;a global but thread-safe weak-reference dictionary), or<br><br>3. implement or can be adapt to the ``overloading.IAspectOwner``
<br>&nbsp;&nbsp;&nbsp;&nbsp;interface (technically, #1 or #2 imply this)<br><br>Subclassing ``Aspect`` creates an adapter class whose state is tied<br>to the life of the adapted object.<br><br>For example, suppose you would like to count all the times a certain
<br>method is called on instances of ``Target`` (a classic AOP example).<br>You might do something like::<br><br>&nbsp;&nbsp;&nbsp;&nbsp; from overloading import Aspect<br><br>&nbsp;&nbsp;&nbsp;&nbsp; class Count(Aspect):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; count = 0<br><br>&nbsp;&nbsp;&nbsp;&nbsp; @after(
Target.some_method)<br>&nbsp;&nbsp;&nbsp;&nbsp; def count_after_call(self, *args, **kw):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Count(self).count += 1<br><br>The above code will keep track of the number of times that<br>``Target.some_method()`` is successfully called (
i.e., it will not<br>count errors).&nbsp;&nbsp;Other code can then access the count using<br>``Count(someTarget).count``.<br><br>``Aspect`` instances can of course have ``__init__`` methods, to<br>initialize any data structures.&nbsp;&nbsp;They can use either ``__slots__``
<br>or dictionary-based attributes for storage.<br><br>While this facility is rather primitive compared to a full-featured<br>AOP tool like AspectJ, persons who wish to build pointcut libraries<br>or other AspectJ-like features can certainly use ``Aspect`` objects
<br>and method-combination decorators as a base for more expressive AOP<br>tools.<br><br>XXX spec out full aspect API, including keys, N-to-1 aspects, manual<br>&nbsp;&nbsp;&nbsp;&nbsp; attach/detach/delete of aspect instances, and the ``IAspectOwner``
<br>&nbsp;&nbsp;&nbsp;&nbsp; interface.<br><br><br>Extension API<br>=============<br><br>TODO: explain how all of these work<br><br>implies(o1, o2)<br><br>declare_implementation(iface, class)<br><br>predicate_signatures(ob)<br><br>parse_rule(ruleset, body, predicate, actiontype, localdict, globaldict)
<br><br>combine_actions(a1, a2)<br><br>rules_for(f)<br><br>Rule objects<br><br>ActionDef objects<br><br>RuleSet objects<br><br>Method objects<br><br>MethodList objects<br><br>IAspectOwner<br><br><br><br>Implementation Notes
<br>====================<br><br>Most of the functionality described in this PEP is already implemented<br>in the in-development version of the PEAK-Rules framework.&nbsp;&nbsp;In<br>particular, the basic overloading and method combination framework
<br>(minus the ``@overload`` decorator) already exists there.&nbsp;&nbsp;The<br>implementation of all of these features in ``peak.rules.core`` is 656<br>lines of Python at this writing.<br><br>``peak.rules.core`` currently relies on the DecoratorTools and
<br>BytecodeAssembler modules, but both of these dependencies can be<br>replaced, as DecoratorTools is used mainly for Python 2.3<br>compatibility and to implement structure types (which can be done<br>with named tuples in later versions of Python).&nbsp;&nbsp;The use of
<br>BytecodeAssembler can be replaced using an &quot;exec&quot; or &quot;compile&quot;<br>workaround, given a reasonable effort.&nbsp;&nbsp;(It would be easier to do this<br>if the ``func_closure`` attribute of function objects was writable.)
<br><br>The ``Interface`` class has been previously prototyped, but is not<br>included in PEAK-Rules at the present time.<br><br>The &quot;implicit class rule&quot; has previously been implemented in the<br>RuleDispatch library.&nbsp;&nbsp;However, it relies on the ``__metaclass__``
<br>hook that is currently eliminated in PEP 3115.<br><br>I don&#39;t currently know how to make ``@overload`` play nicely with<br>``classmethod`` and ``staticmethod`` in class bodies.&nbsp;&nbsp;It&#39;s not really<br>clear if it needs to, however.
<br><br><br>Copyright<br>=========<br><br>This document has been placed in the public domain.<br><br>_______________________________________________<br>Python-3000 mailing list<br><a href="mailto:Python-3000@python.org">Python-3000@python.org
</a><br><a href="http://mail.python.org/mailman/listinfo/python-3000">http://mail.python.org/mailman/listinfo/python-3000</a><br>Unsubscribe: <a href="http://mail.python.org/mailman/options/python-3000/monpublic%40gmail.com">
http://mail.python.org/mailman/options/python-3000/monpublic%40gmail.com</a><br></blockquote></div><br>