扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者: LavenderSs 来源:CSDN 2008年2月14日
关键字: java Interfaces python
我的文章《Java is not Python, either》看起来引发了一些争议,回想一下我想是我的错。虽然我在文章中说,“一些像Zope, Twisted, and PEAK这样的框架都有接口,但由于他们并不是这些语言或者标准库中的一部分,对于Python开发人员来说它们就像不存在一样”,然而就像所有人都知道我在说什么一样我接着继续讨论它们。当然了!所以,让我们来看看我首先谈到的问题,就是如今关于Python的接口有何用处以及它与Java的接口在使用方面区别在哪里。
首先,Python的接口比Java更具有动态性。比如,Java不支持你随意改变一个类或实例所支持的接口,而Python支持。Java要求以类的形式编写一个接口,而Python允许利用一个类,一个对象随意地生成它,几乎接近于任何你想要的。
对于Java,如果你不这样获得或者实现一个接口,你的代码便不会编译通过。对于Python,只要你的代码“能像鸭子一样叫唤”,它仍是一个鸭子(除非你将它传递给一个需要内省或适应接口的函数,后面将展开来说)。
所以,如果你习惯Java的接口,你也许会对这一点表示疑惑:如果Python的接口如此自由随意并且没有Java能做而Python不能做的事情,我们到底还要它们干什么?
我很高兴你能提出这个问题。(ok,我很高兴我将这个问题以你的口吻提出来)。对于Python,接口有三点用处,以其重要性的降序粗略的排列为:
1. 文档(Documentation)
2. 适应性(Adaptation)
3. 内省(Introspection)(我认为这是一个糟糕的想法,可是得到了广泛的认同)
(译者注: 自省是指代码可以查看内存中以对象形式存在的其它模块和函数,获取它们的信息,并对它们进行操作)
现在,我假设你提出的下一个问题是,“为什么针对文档我需要接口?为什么不去写文档?”当然,那很大程度取决于文档是给谁看的和是干什么用的。
如果你在写一个库或者一个脚本,一个人需要了解的全部就是如何去使用它。他们将调用你的代码,而不是你调用他们的。他们不会去修改或者扩展你的代码,即便他们这么做了,他们靠的是自己。所以,如果他们破坏了它,***(原文为screw 'em,为骂人的话)。他们实在不需要接口的约束规则。
但是,如果你创建的是一个框架,情况就不一样了。一个框架是一个库,它调用框架用户的代码。现在,有人要编写代码以有效地作为你的库的一部分,他们需要了解有关他们的代码是如何被调用的一些事。对于一个库而言,如果代码作者或者维护人员是唯一知道他们代码的内部依从关系的人,这是没有问题的。然而对于一个框架,用户实际上是若干联合起来的作者和维护人员。他们需要更多具体的信息。
例如,考虑一下PEP 234,迭代器协议。对于迭代器这种情况,我们可以把Python本身看造成“框架”,它将调用用户的代码来执行迭代器。利用眼下流行的Python接口的惯用语句,我们用一对Python接口来看看迭代器协议长什么样子:
如果与PEP 234作比较,这些用来描述针对Python迭代器的方法的内容基本上是相同的。但是,它还具有一些原文所没有的优点。首先,它与实际的实现在外形上看十分接近,为了开始实现你的方法,你可以从它那进行剪切和粘贴,而不必将“不含参数”转换为一个方法签名(译者注:方法签名是方法的参数列表中参数类型的序列,它最初的用途大概是用来结合方法名称识别特定的方法重载的);文档原文会涉及到IIterable或IIterator对象,而不会(像PEP 234那样)讨论关于“协议1”和“协议2”的事或者通过其他方式提出来正在讨论的事;你可以很轻松的浏览特定函数的定义;一种事物显然是其他事物的特例,如此等等。
现在,这个问题的关键之处并不是说PEP 234应该具有接口。要那么讲就太愚蠢了。虽说曾经有段时间我对在PEP 333中使用接口很感兴趣,可之后就因为一个很简单的问题而退缩了,那就是等到我完成对对详述接口的规约的解释时,忍受用不怎么精确(和简洁)的方式定义它们相比,它将会占据更大的篇幅。所以现在对于PEP就没有必要包含接口,因为它们与大多数技术爱好者们是无关的。任何已知的PEP作者尝试这样做都将扼杀PEP,因为他们将花费更多的时间来回答有关接口定义而非PEP内容方面的问题。
虽然那些规约从我给的例子中可以被逆向设计(reverse-engineere),我没有解释它们。这倒提醒了我。使用接口的Python社区的普遍习惯是以字母‘I’打头来命名接口。这把一些人拦在了围墙之外,但大多数是肯定这一点的。因为它能够清晰地确定一个东西是否是一个接口。并且这种确定对于文档和讨论是大有裨益的。
其次,按照惯例,一个接口中的方法是不包含“self”的。他们以调用者的角度反映了一个方法签名,而且你不用将“self”传给方法。尽管通过复制粘贴来实现它们的时侯这是件痛苦的事情,但是之后当你编写调用这些方法的代码的时候进行剪切粘贴也将十分容易。所以,一个人可以以任一种方式添加或删除“self”,并且目前的规约可以在实现的时候添加“self”。
再次,接口之间的继承是指继承的接口要拥有与基接口全部相同的行为,但可能会在其调用者上提供额外的功能或松散的约束。例如,一个在子接口里重写了的方法可能会带有附加的参数,只要参数是可选的。因此,一个基接口的调用签名对于派生接口仍是可用的。如果一个接口有任何与其基接口不兼容的调用签名,该接口就不应该由基接口派生而来。取而代之的公有方法(common methods)应该被提取进一个公有基接口中或在一个新接口中进行复写。
在接口里面也可以详细说明一个对象所希望出现的属性。一个简单方法是加入类似这样的代码:
anAttribute = property(doc="""Describe the attribute here""")
到接口的定义中。PyProtocols和zope.
的接口也提供了你可以使用的对象的属性。
最后,函数或其他可调用的对象以
__call__ method来描述。例如,一个PEP 333应用程序对象可大略简写成这样:
这比在PEP 333种关于调用可调用对象(call callables)的详细解释要简短的多。但是,由于这个调用签名可能是由一个类、函数、方法或其他任何可调用对象实现的,其含义是一样的。
现在尽管它代替不了一个例子,上述这个接口给出了一个WSGI“应用程序对象”的简明概述。再注意一下,当我描述那些参数(正是它们使我在技术规范中拥有绝对的精度),但没有立即解释那些其他概念是什么的时候,我是如何谈及其他接口的。虽然你对一些环境和标题输出有了一个总体认识,但你也知道当你需要的时候你能够搜索浏览到'class IEnviron' 或 'class IHeaderOutput'的相关细节。
这就是我在写PEP 333时遗漏的一些有关精度和灵活性的详细介绍。但我真的不想混淆那些因接口引发的没完没了的争端。我也特别不想人们抛弃PEP仅仅是因为“接口很差劲,不该放在Python之中”。
但是,当我谈到Pyhon需要接口的时候,我的主要意思是在讲:文档的约定(conventions for documentation)。特别是支持需要协同完成的详细技术说明的文档,像PEP 234或PEP 333。随着时间的流逝,我想我们将看到越来越多的Python项目将需要能够讨论它们的接口是什么。当然如果我们都能以相同的方式进行讨论的话那就更好了。
查看本文来源
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者