619 lines
43 KiB
HTML
619 lines
43 KiB
HTML
|
|
|||
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="utf-8">
|
|||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|||
|
<meta name="color-scheme" content="light dark">
|
|||
|
<title>PEP 245 – Python Interface Syntax | peps.python.org</title>
|
|||
|
<link rel="shortcut icon" href="../_static/py.png">
|
|||
|
<link rel="canonical" href="https://peps.python.org/pep-0245/">
|
|||
|
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
|||
|
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
|||
|
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
|||
|
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
|||
|
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
|||
|
<meta property="og:title" content='PEP 245 – Python Interface Syntax | peps.python.org'>
|
|||
|
<meta property="og:description" content="This PEP describes a proposed syntax for creating interface objects in Python.">
|
|||
|
<meta property="og:type" content="website">
|
|||
|
<meta property="og:url" content="https://peps.python.org/pep-0245/">
|
|||
|
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
|||
|
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
|||
|
<meta property="og:image:alt" content="Python PEPs">
|
|||
|
<meta property="og:image:width" content="200">
|
|||
|
<meta property="og:image:height" content="200">
|
|||
|
<meta name="description" content="This PEP describes a proposed syntax for creating interface objects in Python.">
|
|||
|
<meta name="theme-color" content="#3776ab">
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|||
|
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
|||
|
<title>Following system colour scheme</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
|||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
<circle cx="12" cy="12" r="9"></circle>
|
|||
|
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
|||
|
<title>Selected dark colour scheme</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
|||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
|||
|
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
|||
|
<title>Selected light colour scheme</title>
|
|||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
|||
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|||
|
<circle cx="12" cy="12" r="5"></circle>
|
|||
|
<line x1="12" y1="1" x2="12" y2="3"></line>
|
|||
|
<line x1="12" y1="21" x2="12" y2="23"></line>
|
|||
|
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
|||
|
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
|||
|
<line x1="1" y1="12" x2="3" y2="12"></line>
|
|||
|
<line x1="21" y1="12" x2="23" y2="12"></line>
|
|||
|
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
|||
|
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
|||
|
</svg>
|
|||
|
</symbol>
|
|||
|
</svg>
|
|||
|
<script>
|
|||
|
|
|||
|
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
|||
|
</script>
|
|||
|
<section id="pep-page-section">
|
|||
|
<header>
|
|||
|
<h1>Python Enhancement Proposals</h1>
|
|||
|
<ul class="breadcrumbs">
|
|||
|
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
|||
|
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
|||
|
<li>PEP 245</li>
|
|||
|
</ul>
|
|||
|
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
|||
|
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
|||
|
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
|||
|
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
|||
|
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
|||
|
</button>
|
|||
|
</header>
|
|||
|
<article>
|
|||
|
<section id="pep-content">
|
|||
|
<h1 class="page-title">PEP 245 – Python Interface Syntax</h1>
|
|||
|
<dl class="rfc2822 field-list simple">
|
|||
|
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
|||
|
<dd class="field-odd">Michel Pelletier <michel at users.sourceforge.net></dd>
|
|||
|
<dt class="field-even">Status<span class="colon">:</span></dt>
|
|||
|
<dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</abbr></dd>
|
|||
|
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
|||
|
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
|||
|
<dt class="field-even">Created<span class="colon">:</span></dt>
|
|||
|
<dd class="field-even">11-Jan-2001</dd>
|
|||
|
<dt class="field-odd">Python-Version<span class="colon">:</span></dt>
|
|||
|
<dd class="field-odd">2.2</dd>
|
|||
|
<dt class="field-even">Post-History<span class="colon">:</span></dt>
|
|||
|
<dd class="field-even">21-Mar-2001</dd>
|
|||
|
</dl>
|
|||
|
<hr class="docutils" />
|
|||
|
<section id="contents">
|
|||
|
<details><summary>Table of Contents</summary><ul class="simple">
|
|||
|
<li><a class="reference internal" href="#rejection-notice">Rejection Notice</a></li>
|
|||
|
<li><a class="reference internal" href="#introduction">Introduction</a></li>
|
|||
|
<li><a class="reference internal" href="#overview">Overview</a></li>
|
|||
|
<li><a class="reference internal" href="#the-problem">The Problem</a></li>
|
|||
|
<li><a class="reference internal" href="#overview-of-the-interface-syntax">Overview of the Interface Syntax</a></li>
|
|||
|
<li><a class="reference internal" href="#interface-assertion">Interface Assertion</a></li>
|
|||
|
<li><a class="reference internal" href="#formal-interface-syntax">Formal Interface Syntax</a></li>
|
|||
|
<li><a class="reference internal" href="#classes-and-interfaces">Classes and Interfaces</a></li>
|
|||
|
<li><a class="reference internal" href="#interface-aware-built-ins">Interface-aware built-ins</a></li>
|
|||
|
<li><a class="reference internal" href="#backward-compatibility">Backward Compatibility</a></li>
|
|||
|
<li><a class="reference internal" href="#summary-of-proposed-changes-to-python">Summary of Proposed Changes to Python</a></li>
|
|||
|
<li><a class="reference internal" href="#risks">Risks</a></li>
|
|||
|
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
|
|||
|
<li><a class="reference internal" href="#goals">Goals</a></li>
|
|||
|
<li><a class="reference internal" href="#syntax">Syntax</a></li>
|
|||
|
<li><a class="reference internal" href="#architecture">Architecture</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#dissenting-opinion">Dissenting Opinion</a></li>
|
|||
|
<li><a class="reference internal" href="#references">References</a></li>
|
|||
|
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
|||
|
</ul>
|
|||
|
</details></section>
|
|||
|
<div class="admonition note">
|
|||
|
<p class="admonition-title">Note</p>
|
|||
|
<p>The no-longer-available Zope interfaces wiki page
|
|||
|
(<code class="docutils literal notranslate"><span class="pre">https://www.zope.org/Wikis/Interfaces</span></code>) originally linked here,
|
|||
|
containing links to further resources for this PEP,
|
|||
|
can be <a class="reference external" href="https://web.archive.org/web/20050327013919/http://www.zope.org/Wikis/Interfaces/FrontPage">found on the Wayback Machine archive</a>.
|
|||
|
Also, the Interface-Dev Zope mailing list on which this PEP was discussed
|
|||
|
was shut down, but <a class="reference external" href="https://mail.zope.dev/pipermail/interface-dev/">its archives remain available</a>.</p>
|
|||
|
</div>
|
|||
|
<section id="rejection-notice">
|
|||
|
<h2><a class="toc-backref" href="#rejection-notice" role="doc-backlink">Rejection Notice</a></h2>
|
|||
|
<p>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, <a class="pep reference internal" href="../pep-0246/" title="PEP 246 – Object Adaptation">PEP 246</a> is
|
|||
|
being rejected in favor of something completely different; interfaces
|
|||
|
won’t play a role in adaptation or whatever will replace it. GvR.</p>
|
|||
|
</section>
|
|||
|
<section id="introduction">
|
|||
|
<h2><a class="toc-backref" href="#introduction" role="doc-backlink">Introduction</a></h2>
|
|||
|
<p>This PEP describes a proposed syntax for creating interface
|
|||
|
objects in Python.</p>
|
|||
|
</section>
|
|||
|
<section id="overview">
|
|||
|
<h2><a class="toc-backref" href="#overview" role="doc-backlink">Overview</a></h2>
|
|||
|
<p>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 <a class="footnote-reference brackets" href="#id5" id="id1">[1]</a>.</p>
|
|||
|
<p>Around the end of 2000, Digital Creations began thinking about
|
|||
|
better component model designs for Zope <a class="footnote-reference brackets" href="#id6" id="id2">[2]</a>. 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.</p>
|
|||
|
<p>The syntax proposed by this PEP relies on syntax enhancements
|
|||
|
describe in <a class="pep reference internal" href="../pep-0232/" title="PEP 232 – Function Attributes">PEP 232</a> and describes an underlying framework
|
|||
|
which <a class="pep reference internal" href="../pep-0233/" title="PEP 233 – Python Online Help">PEP 233</a> 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 <a class="footnote-reference brackets" href="#id7" id="id3">[3]</a>.</p>
|
|||
|
</section>
|
|||
|
<section id="the-problem">
|
|||
|
<h2><a class="toc-backref" href="#the-problem" role="doc-backlink">The Problem</a></h2>
|
|||
|
<p>Interfaces are important because they solve a number of problems
|
|||
|
that arise while developing software:</p>
|
|||
|
<ul class="simple">
|
|||
|
<li>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 <code class="docutils literal notranslate"><span class="pre">__getitem__</span></code> 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.</li>
|
|||
|
<li>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.</li>
|
|||
|
<li>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).</li>
|
|||
|
</ul>
|
|||
|
<p>There are also a number of documentation problems that interfaces
|
|||
|
try to solve.</p>
|
|||
|
<ul class="simple">
|
|||
|
<li>Developers waste a lot of time looking at the source code of
|
|||
|
your system to figure out how objects work.</li>
|
|||
|
<li>Developers who are new to your system may misunderstand how your
|
|||
|
objects work, causing, and possibly propagating, usage errors.</li>
|
|||
|
<li>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”.</li>
|
|||
|
<li>Code inspection can be hard, and very discouraging to novice
|
|||
|
programmers trying to properly understand code written by gurus.</li>
|
|||
|
<li>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.</li>
|
|||
|
</ul>
|
|||
|
<p>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.</p>
|
|||
|
<p>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.</p>
|
|||
|
<p>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.</p>
|
|||
|
</section>
|
|||
|
<section id="overview-of-the-interface-syntax">
|
|||
|
<h2><a class="toc-backref" href="#overview-of-the-interface-syntax" role="doc-backlink">Overview of the Interface Syntax</a></h2>
|
|||
|
<p>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.</p>
|
|||
|
<p>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:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interface</span> <span class="n">CountFishInterface</span><span class="p">:</span>
|
|||
|
<span class="s2">"Fish counting interface"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">oneFish</span><span class="p">():</span>
|
|||
|
<span class="s2">"Increments the fish count by one"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">twoFish</span><span class="p">():</span>
|
|||
|
<span class="s2">"Increments the fish count by two"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">getFishCount</span><span class="p">():</span>
|
|||
|
<span class="s2">"Returns the fish count"</span>
|
|||
|
|
|||
|
<span class="n">interface</span> <span class="n">ColorFishInterface</span><span class="p">:</span>
|
|||
|
<span class="s2">"Fish coloring interface"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">redFish</span><span class="p">():</span>
|
|||
|
<span class="s2">"Sets the current fish color to red"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">blueFish</span><span class="p">():</span>
|
|||
|
<span class="s2">"Sets the current fish color to blue"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">getFishColor</span><span class="p">():</span>
|
|||
|
<span class="s2">"This returns the current fish color"</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This code, when evaluated, will create two interfaces called
|
|||
|
<code class="docutils literal notranslate"><span class="pre">CountFishInterface</span></code> and <code class="docutils literal notranslate"><span class="pre">ColorFishInterface</span></code>. These interfaces
|
|||
|
are defined by the <code class="docutils literal notranslate"><span class="pre">interface</span></code> statement.</p>
|
|||
|
<p>The prose documentation for the interfaces and their methods come
|
|||
|
from doc strings. The method signature information comes from the
|
|||
|
signatures of the <code class="docutils literal notranslate"><span class="pre">def</span></code> 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.</p>
|
|||
|
<p>You can also create interfaces that “extend” other interfaces.
|
|||
|
Here, you can see a new type of Interface that extends the
|
|||
|
CountFishInterface and ColorFishInterface:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interface</span> <span class="n">FishMarketInterface</span><span class="p">(</span><span class="n">CountFishInterface</span><span class="p">,</span> <span class="n">ColorFishInterface</span><span class="p">):</span>
|
|||
|
<span class="s2">"This is the documentation for the FishMarketInterface"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">getFishMonger</span><span class="p">():</span>
|
|||
|
<span class="s2">"Returns the fish monger you can interact with"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">hireNewFishMonger</span><span class="p">(</span><span class="n">name</span><span class="p">):</span>
|
|||
|
<span class="s2">"Hire a new fish monger"</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">buySomeFish</span><span class="p">(</span><span class="n">quantity</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
|
|||
|
<span class="s2">"Buy some fish at the market"</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>The FishMarketInterface extends upon the CountFishInterface and
|
|||
|
ColorfishInterface.</p>
|
|||
|
</section>
|
|||
|
<section id="interface-assertion">
|
|||
|
<h2><a class="toc-backref" href="#interface-assertion" role="doc-backlink">Interface Assertion</a></h2>
|
|||
|
<p>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:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">FishError</span><span class="p">(</span><span class="n">Error</span><span class="p">):</span>
|
|||
|
<span class="k">pass</span>
|
|||
|
|
|||
|
<span class="k">class</span> <span class="nc">FishMarket</span> <span class="n">implements</span> <span class="n">FishMarketInterface</span><span class="p">:</span>
|
|||
|
<span class="n">number</span> <span class="o">=</span> <span class="mi">0</span>
|
|||
|
<span class="n">color</span> <span class="o">=</span> <span class="kc">None</span>
|
|||
|
<span class="n">monger_name</span> <span class="o">=</span> <span class="s1">'Crusty Barnacles'</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">number</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">=</span> <span class="n">number</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">color</span> <span class="o">=</span> <span class="n">color</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">oneFish</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">+=</span> <span class="mi">1</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">twoFish</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">number</span> <span class="o">+=</span> <span class="mi">2</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">redFish</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">color</span> <span class="o">=</span> <span class="s1">'red'</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">blueFish</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">color</span> <span class="o">=</span> <span class="s1">'blue'</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">getFishCount</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">number</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">getFishColor</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">color</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">getFishMonger</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
|
|||
|
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">monger_name</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">hireNewFishMonger</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">name</span><span class="p">):</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">monger_name</span> <span class="o">=</span> <span class="n">name</span>
|
|||
|
|
|||
|
<span class="k">def</span> <span class="nf">buySomeFish</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">quantity</span><span class="o">=</span><span class="mi">1</span><span class="p">):</span>
|
|||
|
<span class="k">if</span> <span class="n">quantity</span> <span class="o">></span> <span class="bp">self</span><span class="o">.</span><span class="n">count</span><span class="p">:</span>
|
|||
|
<span class="k">raise</span> <span class="n">FishError</span><span class="p">(</span><span class="s2">"There's not enough fish"</span><span class="p">)</span>
|
|||
|
<span class="bp">self</span><span class="o">.</span><span class="n">count</span> <span class="o">-=</span> <span class="n">quantity</span>
|
|||
|
<span class="k">return</span> <span class="n">quantity</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This new class, FishMarket defines a concrete class which
|
|||
|
implements the FishMarketInterface. The object following the
|
|||
|
<code class="docutils literal notranslate"><span class="pre">implements</span></code> statement is called an “interface assertion”. An
|
|||
|
interface assertion can be either an interface object, or tuple of
|
|||
|
interface assertions.</p>
|
|||
|
<p>The interface assertion provided in a <code class="docutils literal notranslate"><span class="pre">class</span></code> statement like this
|
|||
|
is stored in the class’s <code class="docutils literal notranslate"><span class="pre">__implements__</span></code> 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:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">FishMarket</span>
|
|||
|
<span class="go"><class FishMarket at 8140f50></span>
|
|||
|
<span class="gp">>>> </span><span class="n">FishMarket</span><span class="o">.</span><span class="n">__implements__</span>
|
|||
|
<span class="go">(<Interface FishMarketInterface at 81006f0>,)</span>
|
|||
|
<span class="gp">>>> </span><span class="n">f</span> <span class="o">=</span> <span class="n">FishMarket</span><span class="p">(</span><span class="mi">6</span><span class="p">,</span> <span class="s1">'red'</span><span class="p">)</span>
|
|||
|
<span class="gp">>>> </span><span class="n">implements</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">FishMarketInterface</span><span class="p">)</span>
|
|||
|
<span class="go">1</span>
|
|||
|
<span class="gp">>>></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>A class can realize more than one interface. For example, say you
|
|||
|
had an interface called <code class="docutils literal notranslate"><span class="pre">ItemInterface</span></code> 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:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">FishMarket</span> <span class="n">implements</span> <span class="n">FishMarketInterface</span><span class="p">,</span> <span class="n">ItemInterface</span><span class="p">:</span>
|
|||
|
<span class="c1"># ...</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>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:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">MyFishMarket</span> <span class="n">implements</span> <span class="n">FishMarketInterface</span><span class="p">,</span> <span class="n">ItemInterface</span><span class="p">:</span>
|
|||
|
<span class="c1"># ...</span>
|
|||
|
|
|||
|
<span class="k">class</span> <span class="nc">YourFishMarket</span> <span class="n">implements</span> <span class="n">FooInterface</span><span class="p">,</span> <span class="n">MyFishMarket</span><span class="o">.</span><span class="n">__implements__</span><span class="p">:</span>
|
|||
|
<span class="c1"># ...</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This new class YourFishMarket, asserts that it implements the
|
|||
|
FooInterface, as well as the interfaces implemented by the
|
|||
|
MyFishMarket class.</p>
|
|||
|
<p>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:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">FooInterface</span>
|
|||
|
|
|||
|
<span class="n">FooInterface</span><span class="p">,</span> <span class="p">(</span><span class="n">BarInterface</span><span class="p">,</span> <span class="n">BobInterface</span><span class="p">)</span>
|
|||
|
|
|||
|
<span class="n">FooInterface</span><span class="p">,</span> <span class="p">(</span><span class="n">BarInterface</span><span class="p">,</span> <span class="p">(</span><span class="n">BobInterface</span><span class="p">,</span> <span class="n">MyClass</span><span class="o">.</span><span class="n">__implements__</span><span class="p">))</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>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.</p>
|
|||
|
<p>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
|
|||
|
<code class="docutils literal notranslate"><span class="pre">deferred</span></code> method that returns a deferred class that implements
|
|||
|
this behavior:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">FM</span> <span class="o">=</span> <span class="n">FishMarketInterface</span><span class="o">.</span><span class="n">deferred</span><span class="p">()</span>
|
|||
|
<span class="gp">>>> </span><span class="k">class</span> <span class="nc">MyFM</span><span class="p">(</span><span class="n">FM</span><span class="p">):</span> <span class="k">pass</span>
|
|||
|
|
|||
|
<span class="gp">>>> </span><span class="n">f</span> <span class="o">=</span> <span class="n">MyFM</span><span class="p">()</span>
|
|||
|
<span class="gp">>>> </span><span class="n">f</span><span class="o">.</span><span class="n">getFishMonger</span><span class="p">()</span>
|
|||
|
<span class="go">Traceback (innermost last):</span>
|
|||
|
File <span class="nb">"<stdin>"</span>, line <span class="m">1</span>, in <span class="n">?</span>
|
|||
|
<span class="gr">Interface.Exceptions.BrokenImplementation</span><span class="w">:</span>
|
|||
|
<span class="x">An object has failed to implement interface FishMarketInterface</span>
|
|||
|
|
|||
|
<span class="x"> The getFishMonger attribute was not provided.</span>
|
|||
|
<span class="gp">>>></span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>This provides for a bit of passive interface enforcement by
|
|||
|
telling you what you forgot to do to implement that interface.</p>
|
|||
|
</section>
|
|||
|
<section id="formal-interface-syntax">
|
|||
|
<h2><a class="toc-backref" href="#formal-interface-syntax" role="doc-backlink">Formal Interface Syntax</a></h2>
|
|||
|
<p>Python syntax is defined in a modified BNF grammar notation
|
|||
|
described in the Python Reference Manual <a class="footnote-reference brackets" href="#id8" id="id4">[4]</a>. This section
|
|||
|
describes the proposed interface syntax using this grammar:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">interfacedef</span><span class="p">:</span> <span class="s2">"interface"</span> <span class="n">interfacename</span> <span class="p">[</span><span class="n">extends</span><span class="p">]</span> <span class="s2">":"</span> <span class="n">suite</span>
|
|||
|
<span class="n">extends</span><span class="p">:</span> <span class="s2">"("</span> <span class="p">[</span><span class="n">expression_list</span><span class="p">]</span> <span class="s2">")"</span>
|
|||
|
<span class="n">interfacename</span><span class="p">:</span> <span class="n">identifier</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>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.</p>
|
|||
|
<p>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.</p>
|
|||
|
<p>This PEP also proposes an extension to Python’s ‘class’ statement:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">classdef</span><span class="p">:</span> <span class="s2">"class"</span> <span class="n">classname</span> <span class="p">[</span><span class="n">inheritance</span><span class="p">]</span> <span class="p">[</span><span class="n">implements</span><span class="p">]</span> <span class="s2">":"</span> <span class="n">suite</span>
|
|||
|
<span class="n">implements</span><span class="p">:</span> <span class="s2">"implements"</span> <span class="n">implist</span>
|
|||
|
<span class="n">implist</span><span class="p">:</span> <span class="n">expression</span><span class="o">-</span><span class="nb">list</span>
|
|||
|
|
|||
|
<span class="n">classname</span><span class="p">,</span>
|
|||
|
<span class="n">inheritance</span><span class="p">,</span>
|
|||
|
<span class="n">suite</span><span class="p">,</span>
|
|||
|
<span class="n">expression</span><span class="o">-</span><span class="nb">list</span><span class="p">:</span> <span class="n">see</span> <span class="n">the</span> <span class="n">Python</span> <span class="n">Reference</span> <span class="n">Manual</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>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.</p>
|
|||
|
<p>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.</p>
|
|||
|
<p>This PEP does not propose any changes to the syntax of function
|
|||
|
definitions or assignments.</p>
|
|||
|
</section>
|
|||
|
<section id="classes-and-interfaces">
|
|||
|
<h2><a class="toc-backref" href="#classes-and-interfaces" role="doc-backlink">Classes and Interfaces</a></h2>
|
|||
|
<p>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.</p>
|
|||
|
<p>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.</p>
|
|||
|
<p>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.</p>
|
|||
|
<p>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.</p>
|
|||
|
</section>
|
|||
|
<section id="interface-aware-built-ins">
|
|||
|
<h2><a class="toc-backref" href="#interface-aware-built-ins" role="doc-backlink">Interface-aware built-ins</a></h2>
|
|||
|
<p>A useful extension to Python’s list of built-in functions in the
|
|||
|
light of interface objects would be <code class="docutils literal notranslate"><span class="pre">implements()</span></code>. 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:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="gp">>>> </span><span class="n">interface</span> <span class="n">FooInterface</span><span class="p">:</span> <span class="k">pass</span>
|
|||
|
<span class="gp">>>> </span><span class="k">class</span> <span class="nc">Foo</span> <span class="n">implements</span> <span class="n">FooInterface</span><span class="p">:</span> <span class="k">pass</span>
|
|||
|
<span class="gp">>>> </span><span class="n">f</span> <span class="o">=</span> <span class="n">Foo</span><span class="p">()</span>
|
|||
|
<span class="gp">>>> </span><span class="n">implements</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="n">FooInterface</span><span class="p">)</span>
|
|||
|
<span class="go">1</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>Currently, this functionality exists in the reference
|
|||
|
implementation as functions in the <code class="docutils literal notranslate"><span class="pre">Interface</span></code> 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 <code class="docutils literal notranslate"><span class="pre">isinstance()</span></code> for classes.</p>
|
|||
|
</section>
|
|||
|
<section id="backward-compatibility">
|
|||
|
<h2><a class="toc-backref" href="#backward-compatibility" role="doc-backlink">Backward Compatibility</a></h2>
|
|||
|
<p>The proposed interface model does not introduce any backward
|
|||
|
compatibility issues in Python. The proposed syntax, however,
|
|||
|
does.</p>
|
|||
|
<p>Any existing code that uses <code class="docutils literal notranslate"><span class="pre">interface</span></code> as an identifier will
|
|||
|
break. There may be other kinds of backwards incompatibility that
|
|||
|
defining <code class="docutils literal notranslate"><span class="pre">interface</span></code> as a new keyword will introduce. This
|
|||
|
extension to Python’s syntax does not change any existing syntax
|
|||
|
in any backward incompatible way.</p>
|
|||
|
<p>The new <code class="docutils literal notranslate"><span class="pre">from</span> <span class="pre">__future__</span></code> Python syntax (<a class="pep reference internal" href="../pep-0236/" title="PEP 236 – Back to the __future__">PEP 236</a>), and the new warning
|
|||
|
framework (<a class="pep reference internal" href="../pep-0230/" title="PEP 230 – Warning Framework">PEP 230</a>) is ideal for resolving this backward
|
|||
|
incompatibility. To use interface syntax now, a developer could
|
|||
|
use the statement:</p>
|
|||
|
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">__future__</span> <span class="kn">import</span> <span class="n">interfaces</span>
|
|||
|
</pre></div>
|
|||
|
</div>
|
|||
|
<p>In addition, any code that uses the keyword <code class="docutils literal notranslate"><span class="pre">interface</span></code> 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 <code class="docutils literal notranslate"><span class="pre">interface</span></code> would raise an exception. This
|
|||
|
period of time is proposed to be 24 months.</p>
|
|||
|
</section>
|
|||
|
<section id="summary-of-proposed-changes-to-python">
|
|||
|
<h2><a class="toc-backref" href="#summary-of-proposed-changes-to-python" role="doc-backlink">Summary of Proposed Changes to Python</a></h2>
|
|||
|
<p>Adding new <code class="docutils literal notranslate"><span class="pre">interface</span></code> keyword and extending class syntax with
|
|||
|
<code class="docutils literal notranslate"><span class="pre">implements</span></code>.</p>
|
|||
|
<p>Extending class interface to include <code class="docutils literal notranslate"><span class="pre">__implements__</span></code>.</p>
|
|||
|
<p>Add ‘implements(obj, interface)’ built-in.</p>
|
|||
|
</section>
|
|||
|
<section id="risks">
|
|||
|
<h2><a class="toc-backref" href="#risks" role="doc-backlink">Risks</a></h2>
|
|||
|
<p>This PEP proposes adding one new keyword to the Python language,
|
|||
|
<code class="docutils literal notranslate"><span class="pre">interface</span></code>. This will break code.</p>
|
|||
|
</section>
|
|||
|
<section id="open-issues">
|
|||
|
<h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2>
|
|||
|
<section id="goals">
|
|||
|
<h3><a class="toc-backref" href="#goals" role="doc-backlink">Goals</a></h3>
|
|||
|
</section>
|
|||
|
<section id="syntax">
|
|||
|
<h3><a class="toc-backref" href="#syntax" role="doc-backlink">Syntax</a></h3>
|
|||
|
</section>
|
|||
|
<section id="architecture">
|
|||
|
<h3><a class="toc-backref" href="#architecture" role="doc-backlink">Architecture</a></h3>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<section id="dissenting-opinion">
|
|||
|
<h2><a class="toc-backref" href="#dissenting-opinion" role="doc-backlink">Dissenting Opinion</a></h2>
|
|||
|
<p>This PEP has not yet been discussed on python-dev.</p>
|
|||
|
</section>
|
|||
|
<section id="references">
|
|||
|
<h2><a class="toc-backref" href="#references" role="doc-backlink">References</a></h2>
|
|||
|
<aside class="footnote-list brackets">
|
|||
|
<aside class="footnote brackets" id="id5" role="doc-footnote">
|
|||
|
<dt class="label" id="id5">[<a href="#id1">1</a>]</dt>
|
|||
|
<dd><a class="reference external" href="https://mail.python.org/pipermail/types-sig/1998-December/date.html">https://mail.python.org/pipermail/types-sig/1998-December/date.html</a></aside>
|
|||
|
<aside class="footnote brackets" id="id6" role="doc-footnote">
|
|||
|
<dt class="label" id="id6">[<a href="#id2">2</a>]</dt>
|
|||
|
<dd><a class="reference external" href="http://www.zope.org">http://www.zope.org</a></aside>
|
|||
|
<aside class="footnote brackets" id="id7" role="doc-footnote">
|
|||
|
<dt class="label" id="id7">[<a href="#id3">3</a>]</dt>
|
|||
|
<dd><a class="reference external" href="http://www.lemburg.com/files/python/mxProxy.html">http://www.lemburg.com/files/python/mxProxy.html</a></aside>
|
|||
|
<aside class="footnote brackets" id="id8" role="doc-footnote">
|
|||
|
<dt class="label" id="id8">[<a href="#id4">4</a>]</dt>
|
|||
|
<dd>Python Reference Manual
|
|||
|
<a class="reference external" href="http://docs.python.org/reference/">http://docs.python.org/reference/</a></aside>
|
|||
|
</aside>
|
|||
|
</section>
|
|||
|
<section id="copyright">
|
|||
|
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
|||
|
<p>This document has been placed in the public domain.</p>
|
|||
|
</section>
|
|||
|
</section>
|
|||
|
<hr class="docutils" />
|
|||
|
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0245.rst">https://github.com/python/peps/blob/main/peps/pep-0245.rst</a></p>
|
|||
|
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0245.rst">2023-09-09 17:39:29 GMT</a></p>
|
|||
|
|
|||
|
</article>
|
|||
|
<nav id="pep-sidebar">
|
|||
|
<h2>Contents</h2>
|
|||
|
<ul>
|
|||
|
<li><a class="reference internal" href="#rejection-notice">Rejection Notice</a></li>
|
|||
|
<li><a class="reference internal" href="#introduction">Introduction</a></li>
|
|||
|
<li><a class="reference internal" href="#overview">Overview</a></li>
|
|||
|
<li><a class="reference internal" href="#the-problem">The Problem</a></li>
|
|||
|
<li><a class="reference internal" href="#overview-of-the-interface-syntax">Overview of the Interface Syntax</a></li>
|
|||
|
<li><a class="reference internal" href="#interface-assertion">Interface Assertion</a></li>
|
|||
|
<li><a class="reference internal" href="#formal-interface-syntax">Formal Interface Syntax</a></li>
|
|||
|
<li><a class="reference internal" href="#classes-and-interfaces">Classes and Interfaces</a></li>
|
|||
|
<li><a class="reference internal" href="#interface-aware-built-ins">Interface-aware built-ins</a></li>
|
|||
|
<li><a class="reference internal" href="#backward-compatibility">Backward Compatibility</a></li>
|
|||
|
<li><a class="reference internal" href="#summary-of-proposed-changes-to-python">Summary of Proposed Changes to Python</a></li>
|
|||
|
<li><a class="reference internal" href="#risks">Risks</a></li>
|
|||
|
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
|
|||
|
<li><a class="reference internal" href="#goals">Goals</a></li>
|
|||
|
<li><a class="reference internal" href="#syntax">Syntax</a></li>
|
|||
|
<li><a class="reference internal" href="#architecture">Architecture</a></li>
|
|||
|
</ul>
|
|||
|
</li>
|
|||
|
<li><a class="reference internal" href="#dissenting-opinion">Dissenting Opinion</a></li>
|
|||
|
<li><a class="reference internal" href="#references">References</a></li>
|
|||
|
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
|||
|
</ul>
|
|||
|
|
|||
|
<br>
|
|||
|
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0245.rst">Page Source (GitHub)</a>
|
|||
|
</nav>
|
|||
|
</section>
|
|||
|
<script src="../_static/colour_scheme.js"></script>
|
|||
|
<script src="../_static/wrap_tables.js"></script>
|
|||
|
<script src="../_static/sticky_banner.js"></script>
|
|||
|
</body>
|
|||
|
</html>
|