Note
The no-longer-available Zope interfaces wiki page(https://www.zope.org/Wikis/Interfaces) originally linked here,containing links to further resources for this PEP,can befound on the Wayback Machine archive.Also, the Interface-Dev Zope mailing list on which this PEP was discussedwas shut down, butits archives remain available.
I’m rejecting this PEP. It’s been five years now. While at somepoint I expect that Python will have interfaces, it would be naiveto expect it to resemble the syntax in this PEP. Also,PEP 246 isbeing rejected in favor of something completely different; interfaceswon’t play a role in adaptation or whatever will replace it. GvR.
This PEP describes a proposed syntax for creating interfaceobjects in Python.
In addition to thinking about adding a static type system toPython, the Types-SIG was also charged to devise an interfacesystem for Python. In December of 1998, Jim Fulton released aprototype interfaces system based on discussions from the SIG.Many of the issues and background information on this discussionand prototype can be found in the SIG archives[1].
Around the end of 2000, Digital Creations began thinking aboutbetter component model designs for Zope[2]. Zope’s futurecomponent model relies heavily on interface objects. This led tofurther development of Jim’s “Scarecrow” interfaces prototype.Starting with version 2.3, Zope comes with an Interface package asstandard software. Zope’s Interface package is used as thereference implementation for this PEP.
The syntax proposed by this PEP relies on syntax enhancementsdescribe inPEP 232 and describes an underlying frameworkwhichPEP 233 could be based upon. There is some work beingdone with regard to interface objects and Proxy objects, so forthose optional parts of this PEP you may want to see[3].
Interfaces are important because they solve a number of problemsthat arise while developing software:
__getitem__ implies both asequence and a mapping (the former with sequential, integerkeys). There is no way for the developer to be explicit aboutwhich protocols the object intends to implement.There are also a number of documentation problems that interfacestry to solve.
Interfaces try to solve these problems by providing a way for youto specify a contractual obligation for your object, documentationon how to use an object, and a built-in mechanism for discoveringthe contract and the documentation.
Python has very useful introspection features. It is well knownthat this makes exploring concepts in the interactive interpretereasier, because Python gives you the ability to look at all kindsof information about the objects: the type, doc strings, instancedictionaries, base classes, unbound methods and more.
Many of these features are oriented toward introspecting, usingand changing the implementation of software, and one of them (“docstrings”) is oriented toward providing documentation. Thisproposal describes an extension to this natural introspectionframework that describes an object’s interface.
For the most part, the syntax of interfaces is very much like thesyntax of classes, but future needs, or needs brought up indiscussion, 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 twodifferent interfaces created with the proposed syntax:
interfaceCountFishInterface:"Fish counting interface"defoneFish():"Increments the fish count by one"deftwoFish():"Increments the fish count by two"defgetFishCount():"Returns the fish count"interfaceColorFishInterface:"Fish coloring interface"defredFish():"Sets the current fish color to red"defblueFish():"Sets the current fish color to blue"defgetFishColor():"This returns the current fish color"
This code, when evaluated, will create two interfaces calledCountFishInterface andColorFishInterface. These interfacesare defined by theinterface statement.
The prose documentation for the interfaces and their methods comefrom doc strings. The method signature information comes from thesignatures of thedef statements. Notice how there is no bodyfor the def statements. The interface does not implement aservice to anything; it merely describes one. Documentationstrings on interfaces and interface methods are mandatory, a‘pass’ statement cannot be provided. The interface equivalent ofa 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 theCountFishInterface and ColorFishInterface:
interfaceFishMarketInterface(CountFishInterface,ColorFishInterface):"This is the documentation for the FishMarketInterface"defgetFishMonger():"Returns the fish monger you can interact with"defhireNewFishMonger(name):"Hire a new fish monger"defbuySomeFish(quantity=1):"Buy some fish at the market"
The FishMarketInterface extends upon the CountFishInterface andColorfishInterface.
The next step is to put classes and interfaces together bycreating a concrete Python class that asserts that it implementsan interface. Here is an example FishMarket component that mightdo this:
classFishError(Error):passclassFishMarketimplementsFishMarketInterface:number=0color=Nonemonger_name='Crusty Barnacles'def__init__(self,number,color):self.number=numberself.color=colordefoneFish(self):self.number+=1deftwoFish(self):self.number+=2defredFish(self):self.color='red'defblueFish(self):self.color='blue'defgetFishCount(self):returnself.numberdefgetFishColor(self):returnself.colordefgetFishMonger(self):returnself.monger_namedefhireNewFishMonger(self,name):self.monger_name=namedefbuySomeFish(self,quantity=1):ifquantity>self.count:raiseFishError("There's not enough fish")self.count-=quantityreturnquantity
This new class, FishMarket defines a concrete class whichimplements the FishMarketInterface. The object following theimplements statement is called an “interface assertion”. Aninterface assertion can be either an interface object, or tuple ofinterface assertions.
The interface assertion provided in aclass statement like thisis stored in the class’s__implements__ class attribute. Afterinterpreting the above example, you would have a class statementthat can be examined like this with an ‘implements’ built-infunction:
>>>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 youhad an interface calledItemInterface that described how anobject worked as an item in a container object. If you wanted toassert that FishMarket instances realized the ItemInterfaceinterface as well as the FishMarketInterface, you can provide aninterface assertion that contained a tuple of interface objects tothe FishMarket class:
classFishMarketimplementsFishMarketInterface,ItemInterface:# ...
Interface assertions can also be used if you want to assert thatone class implements an interface, and all of the interfaces thatanother class implements:
classMyFishMarketimplementsFishMarketInterface,ItemInterface:# ...classYourFishMarketimplementsFooInterface,MyFishMarket.__implements__:# ...
This new class YourFishMarket, asserts that it implements theFooInterface, as well as the interfaces implemented by theMyFishMarket class.
It’s worth going into a little bit more detail about interfaceassertions. An interface assertion is either an interface object,or a tuple of interface assertions. For example:
FooInterfaceFooInterface,(BarInterface,BobInterface)FooInterface,(BarInterface,(BobInterface,MyClass.__implements__))
Are all valid interface assertions. When two interfaces definethe same attributes, the order in which information is preferredin the assertion is from top-to-bottom, left-to-right.
There are other interface proposals that, in the need forsimplicity, have combined the notion of class and interface toprovide simple interface enforcement. Interface objects have adeferred method that returns a deferred class that implementsthis behavior:
>>>FM=FishMarketInterface.deferred()>>>classMyFM(FM):pass>>>f=MyFM()>>>f.getFishMonger()Traceback (innermost last): File"<stdin>", line1, 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 bytelling you what you forgot to do to implement that interface.
Python syntax is defined in a modified BNF grammar notationdescribed in the Python Reference Manual[4]. This sectiondescribes the proposed interface syntax using this grammar:
interfacedef:"interface"interfacename[extends]":"suiteextends:"("[expression_list]")"interfacename:identifier
An interface definition is an executable statement. It firstevaluates the extends list, if present. Each item in the extendslist 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 newlycreated local namespace and the original global namespace. Whenthe interface’s suite finishes execution, its execution frame isdiscarded but its local namespace is saved as interface elements.An interface object is then created using the extends list for thebase interfaces and the saved interface elements. The interfacename is bound to this interface object in the original localnamespace.
This PEP also proposes an extension to Python’s ‘class’ statement:
classdef:"class"classname[inheritance][implements]":"suiteimplements:"implements"implistimplist:expression-listclassname,inheritance,suite,expression-list:seethePythonReferenceManual
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 ofthe Language Reference.
The ‘implements’, if present, is evaluated after inheritance.This must evaluate to an interface specification, which is eitheran interface, or a tuple of interface specifications. If a validinterface specification is present, the assertion is assigned tothe class object’s ‘__implements__’ attribute, as a tuple.
This PEP does not propose any changes to the syntax of functiondefinitions or assignments.
The example interfaces above do not describe any kind of behaviorfor their methods, they just describe an interface that a typicalFishMarket object would realize.
You may notice a similarity between interfaces extending fromother interfaces and classes sub-classing from other classes.This is a similar concept. However it is important to note thatinterfaces extend interfaces and classes subclass classes. Youcannot extend a class or subclass an interface. Classes andinterfaces are separate.
The purpose of a class is to share the implementation of how anobject works. The purpose of an interface is to document how towork with an object, not how the object is implemented. It ispossible to have several different classes with very differentimplementations realize the same interface.
It’s also possible to implement one interface with many classesthat mix in pieces the functionality of the interface or,conversely, it’s possible to have one class implement manyinterfaces. Because of this, interfaces and classes should not beconfused or intermingled.
A useful extension to Python’s list of built-in functions in thelight of interface objects would beimplements(). This builtinwould expect two arguments, an object and an interface, and returna true value if the object implements the interface, falseotherwise. For example:
>>>interfaceFooInterface:pass>>>classFooimplementsFooInterface:pass>>>f=Foo()>>>implements(f,FooInterface)1
Currently, this functionality exists in the referenceimplementation as functions in theInterface package, requiringan “import Interface” to use it. Its existence as a built-inwould be purely for a convenience, and not necessary for usinginterfaces, and analogous toisinstance() for classes.
The proposed interface model does not introduce any backwardcompatibility issues in Python. The proposed syntax, however,does.
Any existing code that usesinterface as an identifier willbreak. There may be other kinds of backwards incompatibility thatdefininginterface as a new keyword will introduce. Thisextension to Python’s syntax does not change any existing syntaxin any backward incompatible way.
The newfrom__future__ Python syntax (PEP 236), and the new warningframework (PEP 230) is ideal for resolving this backwardincompatibility. To use interface syntax now, a developer coulduse the statement:
from__future__importinterfaces
In addition, any code that uses the keywordinterface as anidentifier will be issued a warning from Python. After theappropriate period of time, the interface syntax would becomestandard, the above import statement would do nothing, and anyidentifiers namedinterface would raise an exception. Thisperiod of time is proposed to be 24 months.
Adding newinterface keyword and extending class syntax withimplements.
Extending class interface to include__implements__.
Add ‘implements(obj, interface)’ built-in.
This PEP proposes adding one new keyword to the Python language,interface. This will break code.
This PEP has not yet been discussed on python-dev.
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0245.rst
Last modified:2025-02-01 08:59:27 GMT