首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > perl python >

Python API 设计(二):无绪和一致性

2012-09-20 
Python API 设计(2):无绪和一致性无绪:API 设计的终极目标?在《软件框架设计的艺术》这本书里面,提到一个 AP

Python API 设计(2):无绪和一致性

无绪:API 设计的终极目标

?

在《软件框架设计的艺术》这本书里面,提到一个 API 设计原则,称之为无绪(cluelessness)。

?

无绪是这样一个概念: API 的设计应该尽可能地『自解释』,让客户端程序员(也即是使用者)通过少量学习甚至不学习的情况下,就能使用该 API 。

?

举个例子,购买过电子产品的朋友可能就有过这样的经历:一个设计得好的产品,它的操作应该是完全直观、流畅、开箱即用的,你可以在不看一页产品说明书的情况下,将整个产品的功能弄懂。

?

而如果不幸碰上了设计得不好的产品,你就会发现自己在尝试操作的过程中频繁碰壁,最后只好求助于说明书,客服或者互联网。

?

那么,很明显,设计无绪 API 的目标,就是造出不用看说明手册(文档)就能使用的 API —— 这听上去有点唬人,作为程序员,我们为了学习某样知识,通常需要花费大量的时间阅读各种各样的文档、书本和博客 ——?要学习某样东西,你就必须去读那该死的文档(RTFM),对我们已经成为了一种习惯,那么,自然地,一种不用读文档(或者源码)也能使用的 API ,对程序员来说肯定是非常有诱惑力,同时,这也让我们心生疑惑:这种 API 可能存在吗,如果存在,这种 API 是什么样子?又或者,所谓的『无绪』只是书里面捏造出来的一个子虚乌有的新名词?

?

为了证实无绪 API 的确存在,我在脑海中搜索自己学习各种语言和库 API 的经历,很快,我找到了自己一次使用无绪 API 的经历,那次经历的确是让我印象深刻,只是当时还没有意识到『无绪』的存在,好吧,现在,就让我来说说这事。

?

?

一次使用无绪 API 的经历

?

Django 是最常用的 Python 框架之一,绝大部分 Python 的使用者都有学习它的经历,它和很多大而全的框架一样,有各种各样不尽如人意的小问题,其中一个就是它的模板的解释速度非常慢——这种慢说来有点夸张,几乎不用计时测试,你直接用肉眼就会发现 Django?的模板解释速度非常慢,因此,大部分 Django 的使用者学习 Django 之后的第一件事不是用 Django 去写程序,而是给 Django 换一个模板引擎(笑)。

?

在我学习 Django 的时候,我也遇到了这样的问题,于是,我搜索 Google ,查看各种 Python 模板引擎的信息,经过一番筛选之后,我将目标锁定在 Jinja 和 Mako 两个模板引擎上面,因为网上对它们的反响都不错,而且在很多计时测试当中,它们基本都排在前两位,这样就能保证速度不再是程序的瓶颈。

?

其中,Mako 的特点是非常快,它使用的语法基于 Python 自身,但是增加了一些额外的关键字,整体语法比较复杂。

?

另一方面,Jinja 也比 Django 的模板快不少,但它比起 Mako 还是稍慢一些,但是,我发现 Jinja 的语法完全模仿了 Django 的语法, Jinja 用起来就像 DJango 的模板一样,如果我使用 Jinja 代替 Django 的模板的话,那么我连一行文档读不用看,就可以直接用 Jinja 写程序,因为它的语法和 Django 模板的一样,而在此之前,我已经通过文档学习过 Django 模板的语法。

?

发现了 Jinja 的这个好处之后,我就马上决定使用 Jinja ,而不是 Mako ,原因很简单,我不想使用一种慢得离谱的模板引擎,同时,在足够快的 Jinja 和 非常快的 Mako 之间,我更喜欢让我不再读更多文档、马上就能使用的 Jinja ,如果将来连足够快的 Jinja 都不能满足我的需求的话,我再来学习怎么用 Mako 好了

?

有趣的是,很多 Python 程序员和我的想法一样,通过一些帖子我了解到,在很多时候,大家都喜欢使用 Jinja ,只有当性能真正成为问题的时候,才转向 Mako 。

?

我认为 Jinja 受欢迎的一个很重要的原因是它模仿了 Django 模板的语法,这使得它对那些为数众多的使学习过 Django 的人可以马上”学会“ Jinja ,期间不需要多读一行文档——因此,在这里我们可以说,Jinja 的 API 符合我们上面所说的『无绪』的原则,至少对于那些学习过 Django 模板的人来说是这样,而这样人并不在少数,最终,这种无绪在实践中成为 Jinja 的一种巨大的优势。

?

?

无绪实战(1)

?

好的,通过 Django 和 Jinja 的例子,我们已经见识到了无绪 API 的好处,但是,我们还没具体地知道, 无绪 API 到底是什么样子,还有,该怎么去实现一个无绪 API ?在以下的内容,我就继续以 OORedis 作为例子,从理论转向实战,看看 OORedis 是如何在实践中应用无绪原则的。

?

在前一篇文章中, OORedis 的第二版 API ?基本就是 Redis 命令的直接翻译,比如对 Redis 的 Hash 结构,就有对应的类 Hash 及方法 get 和 set :

?

list_like_object.append(item_1)list_like_object.append(item_2)len(list_like_object) # => 2

?

而要实现一个『无绪化』的、模仿 Python 内置数据结构操作方式的 OORedis API ,我们就要一个不漏地实现相应的魔法方法,但是,这样做起来很麻烦,原因有两个:

?

1. 要实现的魔法方法不少

2. 就算人工地实现了所有所需的魔法方法,但还是很难跟 Python 内置数据结构保持一致,比如说,在异常抛出问题上,人工实现的 API 的异常就很容易和 Python 内置数据结构抛出的异常不同,造成这个问题的主要原因是在人工的 API 和 Python 内置数据结构的 API 之间,没有一个统一的接口

?

幸好,Python 为这类问题提供了一组 ABC 类(abstract base classes),通过继承这些 ABC 类,我们只需要编写一簇魔法方法的最小子集合,就可以获得一整个功能完整的类,而且,这些类的行为几乎完全和相应的 Python 内置数据结构的行为一致。

?

比如说,只要继承 collections.MutableMapping 类,然后实现 __setitem__ 、 __getitem__ 、 __delitem__ 、 __len__ 和 __iter__ 五个魔法方法,我们就可以获得一个和 Python 内置的字典类(dict)功能上别无二致的类,这些类一共有十多二十个常用方法,而真正要写的只有五个 —— OORedis 中的 Dict 类就是这样子实现的,通过五个魔法方法,每个方法平均五六行代码,就这样简单地将 Redis 的哈希表结构的大部分功能完整地实现了。

?

?

一致性

?

在上面的例子中,我们看到 JInja 通过模范 Django 模板的语法,减少了对新手的学习成本,从而使得更多人特别是那些使用过 Django 模板的人更倾向于使用 Jinja 。

?

我们还看到, OORedis 通过继承 ABC 类,高效地模仿了 Python 的内置数据结构的标准库行为,让操作 Redis 数据结构变得像操作 Python 的内置数据结构一样简单。

?

我们说这些 API 是无绪的,因为它们都非常简单易用,只需很少的学习甚至无须学习就可以使用。

?

但是这里还有几个问题要澄清:

?

首先,前文所说的『模仿』在 API 相关术语中还有一个更专业的名词,就是『一致性』,比如我们说 OORedis 『模仿』了 Python 内置数据结构的标准库行为,换种更专业一点的说法,我们可以说 OORedis 的 API 和 Python 内置数据结构的标准库 API 『保持一致』。

?

其次,虽然上面的无绪的例子都是一些关于『一致性』的案例,但是实现无绪并不是只有『保持一致性』这一种方式,比如说,你可以将 API 写得非常简单易用,连小学生都能学会;或者将 API 写得非常通用,通用得 就像 SQL 语句似的;又或者,遵循一种特定的方式来写你的代码,比如 Erlang 的 OTP 和 jQuery 的 callback 函数就是一个例子。诸如此类。

?

总而言之,有很多方法可以让 API (和代码)变得更无绪,但『保持一致性』的确是一种达到『无绪化』目标的快速有效的手段:如果你要写一个库,但是你不确定 API 该怎么写,那么最简单的最方便的无绪方法就是模仿一个人尽皆知的库的 API ,那样的话,你的 API 的学习曲线就会非常的低,最起码对学习过你所模仿的那个库的人来说是这样子的。

?

第三,有时候,有多于一个 API 可供模仿,比如你发现你的库无论是模仿标准库还是外部库都非常适合,这时候,就有一个优先级列表(从先到后,从高到低):

?

1. 和 API 自身保持一致:这是最重要的,无论你模仿的是什么库,关键是内部要保持一种统一的风格。

?

2. 和语言自身或者标准库保持一致:这种一致性非常强大,如果你能做到,你就无敌了——因为所有能熟练使用这种语言(及其标准库)API 的人都可以很容易地使用你的 API 。

?

? ? 另外,语言自身一般会提供一些帮助来让你达到这个层次的一致性的目的,比如之前说的 Python 的 ABC 类和 Ruby 的 Mixin 机制。

?

3. 和常用库保持一直:每一些语言都有一些非常热门的库,这些库的 API 的使用者为数众多,以致于人们对这些库的熟悉程度和标准库相差无几。

?

? ? 比如,如果你使用 Python ,那么模仿 Django 的 API 就是一种不错的选择。如果你使用 Ruby ,那么模仿 Rails 中的库的 API 也不错。又或者,对于 Erlang 程序员, 无论是直接使用或者模仿,?OTP 库都是一个很好的对象。

?

4. 到了这一步,其实就没有什么库是值得模仿的了,和一些小众的库保持一致并不一定能减少你的库的学习难度,不过如果某个库设计得很好,并且和你所写的库相差无几,那么学习一下也无妨。

?

?

总结

?

『无绪』、『一致性』是两个非常强大的技术,它们对于写出让人更容易理解和学习的 API 方面非常有帮助,想一想我们开篇的那个电子产品的例子,你是希望写一个要让人仔细看说明文档才能会用的 API ,还是要写一个上手即用的 API ?

?

如果你的选择是后者的话,那么请谨记, 『无绪』和『一致性』是你的好朋友,下次写 API 的时候,不妨先想想,我要怎么让这个库易用易学起来?怎么让它『无绪』化?

?

如果你真的这样做了的话,我相信你最终完成的 API 比起一个直接『翻译』过来的 API 会更好用,写出的代码也会更漂亮。

?

?

待续

?

函数式编程(functional programming)已经成为一个越来越热的话题了,越来越多的编程语言都在增加各种各样的函数式语言的机制,争取让整个语言变得更健壮,更好用。

?

实际上,不但编程语言可以通过学习函数式编程来提高自身的质量,一个库的 API 也可以。

?

在本系列的下篇文章中,我将讲述一些例子,说明怎么通过将 API 变得更『函数式』化,来让 API 变得更简单、易用和通用,并且,这一切只需要使用 Python 就能做到。

?

?

脚注

?

关于Jinja 和 Django

?

Jinja 的作者 Armin Ronacher 写了一篇文章,分析和比较了 Mako 和 Jinja 以及 Django 的模板:?http://lucumr.pocoo.org/2008/1/1/python-template-engine-comparison/

Jinja 项目上的文档,可以看到,Jinja 非常容易学习,其中和 Django 的差别只有很少:?http://jinja.pocoo.org/docs/switching/

StackOverFlow 上对 Jinja 和 Mako 的比较: ?http://stackoverflow.com/questions/3435972/mako-or-jinja2

关于 Mako 和 Jinja 速度的比较,在这个文章中,新版的 Jinja 速度已经超过了 Mako:?http://techspot.zzzeek.org/2010/11/19/quick-mako-vs.-jinja-speed-test/

?

关于 Python 的魔法方法和 ABC 类

?

Python 的 collections 库中的 ABC 类:?http://docs.python.org/py3k/library/collections.html#abcs-abstract-base-classes

?

Python 的数据模型的描述:?http://docs.python.org/py3k/reference/datamodel.html

?

关于 OORedis

?

OORedis 的项目主页:?https://github.com/huangz1990/ooredis

?

Dict 类的实现代码:?https://github.com/huangz1990/ooredis/blob/master/ooredis/mix/dict.py

?

关于《软件框架设计的艺术》

?

这本书我个人的感觉其实写得一般,我看完整本书获得唯一深刻的概念就是『无绪』两个字了,仅此而已,也许是因为我不是一个 JAVA 程序员吧。(笑)

?

?

?

?

?

?

?

?

?

?

?

?

热点排行