forked from python/peps
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pep-0245.txt
515 lines (378 loc) · 17.2 KB
/
pep-0245.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
PEP: 245
Title: Python Interface Syntax
Version: $Revision$
Last-Modified: $Date$
Author: Michel Pelletier <[email protected]>
Discussions-To: http://www.zope.org/Wikis/Interfaces
Status: Rejected
Type: Standards Track
Content-Type: text/x-rst
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` and describes an underlying framework
which :pep:`233` 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 [3]_.
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 explicit 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 languages provide you with
much more type safety, but are often overly verbose because
objects can only be generalized by common subclassing and used
specifically 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 FishMarketInterface 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, (BarInterface, 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 grammar notation
described in the Python Reference Manual [4]_. 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 (:pep:`236`), and the new warning
framework (:pep:`230`) 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] https://mail.python.org/pipermail/types-sig/1998-December/date.html
.. [2] http://www.zope.org
.. [3] http://www.lemburg.com/files/python/mxProxy.html
.. [4] Python Reference Manual
http://docs.python.org/reference/
Copyright
=========
This document has been placed in the public domain.
..
Local Variables:
mode: indented-text
indent-tabs-mode: nil
End: