2.6 管理API变化

在构造API时很难一蹴而就。API需要不断演化、添加、删除或者修改所提供的功能。

在后面的段落中women将讨论如何管理公共API的变化。公共API是指将应用程序或库暴露给终端用户的API。内部API则有另外的考虑,并且由于它们在内部(也就是说用户不需要直接操作这些API),因而可以任意处理它们:分解、调整或者根据需要任意使用。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

这两种API很容易区分。Python的传统是用下划线作为私有API的前缀,如foo是公共API,而_bar是私有的。

在构建API时,最糟糕的事情莫过于API被突然破坏。Linus Torvalds就因对Linux内核公共API破坏的零容忍而闻名。考虑到如此多的人依赖Linux,可以说他的选择是非常明智的。

Unix平台的库管理系统很复杂,它依赖于soname(http://en.wikipedia.org/wiki/Soname)和细粒度的版本标识符。Python中没有这样的系统,也没有对应的转换。因此完全取决于维护者如何选择正确的版本号和策略。但是,关于如何定义自己的库或应用程序的版本,你依然可以将Unix系统作为你的灵感来源。通常,版本号应该反映出API对用户的影响,大部分开发人员通过主版本号的增加来表示此类变化,但这取决于你对版本号管理的方法,你也可以采用增加小版本号的方式。

不管如何决定,最重要的一步就是在修改API时要通过文档对修改进行详细地记录,包括:

 
  • 记录新的接口;
  • 记录废除的旧的接口;
  • 记录如何升级到新的接口。

旧接口不要立刻删除。实际上,应该尽量长时间地保留旧接口。因为已经明确标识为作废,所以新用户不会去使用它。在维护实在太麻烦时再移除旧接口。API变化的记录见示例2.2.

示例2.2API变化的记录

class Car(object): 
    def turn_left(self): 
        """Turn the car left. 

        .. deprecated:: 1.1 
           Use :func:`turn` instead with the direction argument set to left 
        """ 
        self.turn(direction='left') 

    def turn(self, direction): 
        """Turn the car in some direction. 

        :param direction: The direction to turn to. 
        :type direction: str 
        """ 
        # Write actual code here instead 
        pass

使用Sphinx标记强调修改是个好主意。在构建文档时,用户应该能清楚地知道某个功能不应该再被使用,并且可以直接访问到新功能,并随之解释如何升级旧代码。这个方法的缺点就是,你不能指望开发人员在升级你的Python包到新版本时会去读你的修改日志或者文档。

Python提供了一个很有意思的名为warnings的模块用来解决这一问题。这一模块允许代码发出不同类型的警告信息,如PendingDeprecationWarning和DeprecationWarning。这些警告能够用来通知开发人员某个正在调用的函数已经废弃或即将废弃。这样,开发人员就能够看到他们正在使用旧接口并且应该相应地进行处理1

回到之前的例子,我们可以利用它向用户发出警告,如示例2.3所示。

示例2.3 带警告的API变化的记录

import warnings 

class Car(object): 
    def turn_left(self): 
        """Turn the car left. 

        .. deprecated:: 1.1 
           Use :func:`turn` instead with the direction argument set to "left". 
        """ 
        warnings.warn("turn_left is deprecated, use turn instead", 
                      DeprecationWarning) 
        self.turn(direction='left') 

    def turn(self, direction): 
        """Turn the car in some direction. 

        :param direction: The direction to turn to. 
        :type direction: str 
        """ 
        # Write actual code here instead 
        pass

任何调用了废弃的turn_left函数的代码,都将引发一个警告:

>>> Car().turn_left() 
__main__:8: DeprecationWarning: turn_left is deprecated, use turn instead

 注意

  从Python 2.7开始,DeprecationWarning默认将不显示。可以通过在调用python时指定-W all选项来禁用这一过滤器。关于-W的可用值的更多信息可以参考python手册。

让你的代码告诉开发人员他们的程序正在使用某些最终将要停止工作的东西是明智的,因为这其实也可以自动化。当运行他们的测试集合时,开发人员可以在执行python时使用-W error选项,它会将警告转换为异常。这意味着一个废弃的函数每次被调用时都会有一个错误被抛出,这样使用你的库的开发人员就可以很容易知道如何具体修改他们的代码。

示例2.4 运行python -W error

>>> import warnings 
>>> warnings.warn("This is deprecated", DeprecationWarning) 
Traceback (most recent call last): 
  File "<stdin>", line 1, in <module> 
DeprecationWarning: This is deprecated

1对要和C打交道的Python开发人员来说,这是一个很方便的与__attribute__((deprecated)) GCC扩展的对应物。