skip to navigation
skip to content

Python Wiki

Python Insider Blog

Python 2 or 3?

Help Fund Python

[Python resources in languages other than English]

Non-English Resources

Add an event to this calendar.

Times are shown in UTC/GMT.

Add an event to this calendar.

PEP: 245
Title: Python Interface Syntax
Version: e2b5d1a8a663
Last-Modified:  2009-01-18 09:50:42 +0000 (Sun, 18 Jan 2009)
Author: Michel Pelletier <michel at users.sourceforge.net>
Discussions-To: http://www.zope.org/Wikis/Interfaces
Status: Rejected
Type: Standards Track
Created: 11-Jan-2001
Python-Version: 2.2
Post-History: 21-Mar-2001

Rejection Notice

    I'm rejecting this PEP.  It's been five years now.  While at some
    point I expect that Python will have interfaces, it would be naive
    to expect it to resemble the syntax in this PEP.  Also, PEP 246 is
    being rejected in favor of something completely different; interfaces
    won't play a role in adaptation or whatever will replace it.  GvR.


Introduction

    This PEP describes a proposed syntax for creating interface
    objects in Python.


Overview

    In addition to thinking about adding a static type system to
    Python, the Types-SIG was also charged to devise an interface
    system for Python.  In December of 1998, Jim Fulton released a
    prototype interfaces system based on discussions from the SIG.
    Many of the issues and background information on this discussion
    and prototype can be found in the SIG archives[1].

    Around the end of 2000, Digital Creations began thinking about
    better component model designs for Zope[2].  Zope's future
    component model relies heavily on interface objects.  This led to
    further development of Jim's "Scarecrow" interfaces prototype.
    Starting with version 2.3, Zope comes with an Interface package as
    standard software.  Zope's Interface package is used as the
    reference implementation for this PEP.

    The syntax proposed by this PEP relies on syntax enhancements
    describe in PEP 232 [3] and describes an underlying framework
    which PEP 233 [4] could be based upon.  There is some work being
    done with regard to interface objects and Proxy objects, so for
    those optional parts of this PEP you may want to see[5].


The Problem

    Interfaces are important because they solve a number of problems
    that arise while developing software:

    - There are many implied interfaces in Python, commonly referred
      to as "protocols".  Currently determining those protocols is
      based on implementation introspection, but often that also
      fails.  For example, defining __getitem__ implies both a
      sequence and a mapping (the former with sequential, integer
      keys).  There is no way for the developer to be explict about
      which protocols the object intends to implement.

    - Python is limited, from the developer's point of view, by the
      split between types and classes.  When types are expected, the
      consumer uses code like 'type(foo) == type("")' to determine if
      'foo' is a string.  When instances of classes are expected, the
      consumer uses 'isinstance(foo, MyString)' to determine if 'foo'
      is an instance of the 'MyString' class.  There is no unified
      model for determining if an object can be used in a certain,
      valid way.

    - Python's dynamic typing is very flexible and powerful, but it
      does not have the advantage of static typed languages that
      provide type checking.  Static typed langauges provide you with
      much more type saftey, but are often overly verbose because
      objects can only be generalized by common subclassing and used
      specificly with casting (for example, in Java).

    There are also a number of documentation problems that interfaces
    try to solve.

    - Developers waste a lot of time looking at the source code of
      your system to figure out how objects work.

    - Developers who are new to your system may misunderstand how your
      objects work, causing, and possibly propagating, usage errors.

    - Because a lack of interfaces means usage is inferred from the
      source, developers may end up using methods and attributes that
      are meant for "internal use only".

    - Code inspection can be hard, and very discouraging to novice
      programmers trying to properly understand code written by gurus.

    - A lot of time is wasted when many people try very hard to
      understand obscurity (like undocumented software).  Effort spend
      up front documenting interfaces will save much of this time in
      the end.

    Interfaces try to solve these problems by providing a way for you
    to specify a contractual obligation for your object, documentation
    on how to use an object, and a built-in mechanism for discovering
    the contract and the documentation.

    Python has very useful introspection features.  It is well known
    that this makes exploring concepts in the interactive interpreter
    easier, because Python gives you the ability to look at all kinds
    of information about the objects: the type, doc strings, instance
    dictionaries, base classes, unbound methods and more.

    Many of these features are oriented toward introspecting, using
    and changing the implementation of software, and one of them ("doc
    strings") is oriented toward providing documentation.  This
    proposal describes an extension to this natural introspection
    framework that describes an object's interface.


Overview of the Interface Syntax

    For the most part, the syntax of interfaces is very much like the
    syntax of classes, but future needs, or needs brought up in
    discussion, may define new possibilities for interface syntax.

    A formal BNF description of the syntax is givena later in the PEP,
    for the purposes of illustration, here is an example of two
    different interfaces created with the proposed syntax:

        interface CountFishInterface:
            "Fish counting interface"

            def oneFish():
                "Increments the fish count by one"

            def twoFish():
                "Increments the fish count by two"

            def getFishCount():
                "Returns the fish count"

        interface ColorFishInterface:
            "Fish coloring interface"

            def redFish():
                "Sets the current fish color to red"

            def blueFish():
                "Sets the current fish color to blue"

            def getFishColor():
                "This returns the current fish color" 

    This code, when evaluated, will create two interfaces called
    `CountFishInterface' and `ColorFishInterface'. These interfaces
    are defined by the `interface' statement.

    The prose documentation for the interfaces and their methods come
    from doc strings.  The method signature information comes from the
    signatures of the `def' statements.  Notice how there is no body
    for the def statements.  The interface does not implement a
    service to anything; it merely describes one.  Documentation
    strings on interfaces and interface methods are mandatory, a
    'pass' statement cannot be provided.  The interface equivalent of
    a pass statement is an empty doc string.

    You can also create interfaces that "extend" other interfaces.
    Here, you can see a new type of Interface that extends the
    CountFishInterface and ColorFishInterface:

        interface FishMarketInterface(CountFishInterface, ColorFishInterface):
            "This is the documentation for the FishMarketInterface"

            def getFishMonger():
                "Returns the fish monger you can interact with"

            def hireNewFishMonger(name):
                "Hire a new fish monger"

            def buySomeFish(quantity=1):
                "Buy some fish at the market"

    The FishMarketInteface extends upon the CountFishInterface and
    ColorfishInterface.


Interface Assertion

    The next step is to put classes and interfaces together by
    creating a concrete Python class that asserts that it implements
    an interface.  Here is an example FishMarket component that might
    do this:

        class FishError(Error):
            pass

        class FishMarket implements FishMarketInterface:
            number = 0
            color = None
            monger_name = 'Crusty Barnacles' 

            def __init__(self, number, color):
                self.number = number
                self.color = color

            def oneFish(self):
                self.number += 1

            def twoFish(self):
                self.number += 2

            def redFish(self):
                self.color = 'red'

            def blueFish(self):
                self.color = 'blue'

            def getFishCount(self):
                return self.number

            def getFishColor(self):
                return self.color

            def getFishMonger(self):
                return self.monger_name

            def hireNewFishMonger(self, name):
                self.monger_name = name

            def buySomeFish(self, quantity=1):
                if quantity > self.count:
                    raise FishError("There's not enough fish")
                self.count -= quantity
                return quantity

    This new class, FishMarket defines a concrete class which
    implements the FishMarketInterface.  The object following the
    `implements' statement is called an "interface assertion".  An
    interface assertion can be either an interface object, or tuple of
    interface assertions.

    The interface assertion provided in a `class' statement like this
    is stored in the class's `__implements__' class attribute.  After
    interpreting the above example, you would have a class statement
    that can be examined like this with an 'implements' built-in
    function:

        >>> FishMarket
        <class FishMarket at 8140f50>
        >>> FishMarket.__implements__
        (<Interface FishMarketInterface at 81006f0>,)
        >>> f = FishMarket(6, 'red')
        >>> implements(f, FishMarketInterface)
        1
        >>>

    A class can realize more than one interface.  For example, say you
    had an interface called `ItemInterface' that described how an
    object worked as an item in a container object.  If you wanted to
    assert that FishMarket instances realized the ItemInterface
    interface as well as the FishMarketInterface, you can provide an
    interface assertion that contained a tuple of interface objects to
    the FishMarket class:

        class FishMarket implements FishMarketInterface, ItemInterface:
            # ...

    Interface assertions can also be used if you want to assert that
    one class implements an interface, and all of the interfaces that
    another class implements:

        class MyFishMarket implements FishMarketInterface, ItemInterface:
            # ...

        class YourFishMarket implements FooInterface, MyFishMarket.__implements__:
            # ...

    This new class YourFishMarket, asserts that it implements the
    FooInterface, as well as the interfaces implemented by the
    MyFishMarket class.

    It's worth going into a little bit more detail about interface
    assertions.  An interface assertion is either an interface object,
    or a tuple of interface assertions.  For example:

        FooInterface

        FooInterface, (BarInteface, BobInterface)

        FooInterface, (BarInterface, (BobInterface, MyClass.__implements__))

    Are all valid interface assertions.  When two interfaces define
    the same attributes, the order in which information is preferred
    in the assertion is from top-to-bottom, left-to-right.

    There are other interface proposals that, in the need for
    simplicity, have combined the notion of class and interface to
    provide simple interface enforcement.  Interface objects have a
    `deferred' method that returns a deferred class that implements
    this behavior:

        >>> FM = FishMarketInterface.deferred()
        >>> class MyFM(FM): pass

        >>> f = MyFM()
        >>> f.getFishMonger()
        Traceback (innermost last):
          File "<stdin>", line 1, in ?
        Interface.Exceptions.BrokenImplementation: 
        An object has failed to implement interface FishMarketInterface

                The getFishMonger attribute was not provided.
        >>> 

    This provides for a bit of passive interface enforcement by
    telling you what you forgot to do to implement that interface.


Formal Interface Syntax

    Python syntax is defined in a modified BNF grammer notation
    described in the Python Reference Manual [8].  This section
    describes the proposed interface syntax using this grammar:

        interfacedef:   "interface" interfacename [extends] ":" suite
        extends:        "(" [expression_list] ")"
        interfacename:  identifier

    An interface definition is an executable statement.  It first
    evaluates the extends list, if present.  Each item in the extends
    list should evaluate to an interface object.

    The interface's suite is then executed in a new execution frame
    (see the Python Reference Manual, section 4.1), using a newly
    created local namespace and the original global namespace.  When
    the interface's suite finishes execution, its execution frame is
    discarded but its local namespace is saved as interface elements.
    An interface object is then created using the extends list for the
    base interfaces and the saved interface elements.  The interface
    name is bound to this interface object in the original local
    namespace.

    This PEP also proposes an extension to Python's 'class' statement:

        classdef:    "class" classname [inheritance] [implements] ":" suite
        implements:  "implements" implist
        implist:     expression-list

        classname,
        inheritance,
        suite,
        expression-list:  see the Python Reference Manual

    Before a class' suite is executed, the 'inheritance' and
    'implements' statements are evaluated, if present.  The
    'inheritance' behavior is unchanged as defined in Section 7.6 of
    the Language Reference.

    The 'implements', if present, is evaluated after inheritance.
    This must evaluate to an interface specification, which is either
    an interface, or a tuple of interface specifications.  If a valid
    interface specification is present, the assertion is assigned to
    the class object's '__implements__' attribute, as a tuple.

    This PEP does not propose any changes to the syntax of function
    definitions or assignments.


Classes and Interfaces

    The example interfaces above do not describe any kind of behavior
    for their methods, they just describe an interface that a typical
    FishMarket object would realize.

    You may notice a similarity between interfaces extending from
    other interfaces and classes sub-classing from other classes.
    This is a similar concept.  However it is important to note that
    interfaces extend interfaces and classes subclass classes.  You
    cannot extend a class or subclass an interface.  Classes and
    interfaces are separate.

    The purpose of a class is to share the implementation of how an
    object works.  The purpose of an interface is to document how to
    work with an object, not how the object is implemented.  It is
    possible to have several different classes with very different
    implementations realize the same interface.

    It's also possible to implement one interface with many classes
    that mix in pieces the functionality of the interface or,
    conversely, it's possible to have one class implement many
    interfaces.  Because of this, interfaces and classes should not be
    confused or intermingled.


Interface-aware built-ins

    A useful extension to Python's list of built-in functions in the
    light of interface objects would be `implements()'.  This builtin
    would expect two arguments, an object and an interface, and return
    a true value if the object implements the interface, false
    otherwise.  For example:

        >>> interface FooInterface: pass
        >>> class Foo implements FooInterface: pass
        >>> f = Foo()
        >>> implements(f, FooInterface)
        1

    Currently, this functionality exists in the reference
    implementation as functions in the `Interface' package, requiring
    an "import Interface" to use it.  Its existence as a built-in
    would be purely for a convenience, and not necessary for using
    interfaces, and analogous to `isinstance()' for classes.


Backward Compatibility

    The proposed interface model does not introduce any backward
    compatibility issues in Python.  The proposed syntax, however,
    does.

    Any existing code that uses `interface' as an identifier will
    break.  There may be other kinds of backwards incompatibility that
    defining `interface' as a new keyword will introduce.  This
    extension to Python's syntax does not change any existing syntax
    in any backward incompatible way.

    The new `from __future__' Python syntax[6], and the new warning
    framework [7] is ideal for resolving this backward
    incompatibility.  To use interface syntax now, a developer could
    use the statement:

        from __future__ import interfaces

    In addition, any code that uses the keyword `interface' as an
    identifier will be issued a warning from Python.  After the
    appropriate period of time, the interface syntax would become
    standard, the above import statement would do nothing, and any
    identifiers named `interface' would raise an exception.  This
    period of time is proposed to be 24 months.


Summary of Proposed Changes to Python

    Adding new `interface' keyword and extending class syntax with
    `implements'.

    Extending class interface to include __implements__.

    Add 'implements(obj, interface)' built-in.


Risks

    This PEP proposes adding one new keyword to the Python language,
    `interface'.  This will break code.


Open Issues

    Goals

    Syntax

    Architecture


Dissenting Opinion

    This PEP has not yet been discussed on python-dev.
        

References

    [1] http://mail.python.org/pipermail/types-sig/1998-December/date.html

    [2] http://www.zope.org

    [3] PEP 232, Function Attributes, Warsaw
        http://www.python.org/dev/peps/pep-0232/

    [4] PEP 233, Python Online Help, Prescod
        http://www.python.org/dev/peps/pep-0233/

    [5] http://www.lemburg.com/files/python/mxProxy.html

    [6] PEP 236, Back to the __future__, Peters
        http://www.python.org/dev/peps/pep-0236/

    [7] PEP 230, Warning Framework, van Rossum
        http://www.python.org/dev/peps/pep-0236/


Copyright

    This document has been placed in the public domain.