写点什么

一次由默认参数引起的思考

用户头像
Lart
关注
发布于: 2020 年 08 月 16 日
一次由默认参数引起的思考

题图来自 Pexels 上的 Anna Shvets 拍摄的图片

这是我对本人在语雀上书写的原始文档(https://www.yuque.com/lart/blog/qhllk3)的搬运。

缘起

最近换了新机器。需要将旧设备上的代码和数据迁移过来。关键的一部分是自己论文使用的代码。为了确保代码迁移的完整性,在新机器上对其进行了验证。但是出乎意料,这部分 python 代码竟然输出了不一样的结果。经过多番排查,终于确定了问题所在,那就是其中使用的一个图像处理库——pillow,新机器上安装的是版本 7.2.0,但是旧机器上确实 6.2.1。

在 pillow 的 release note(https://pillow.readthedocs.io/en/stable/releasenotes/7.0.0.html#default-resampling-filter)中可以看到,在 7.0.0 版本中,这个库进行了不兼容的修改。将用来放缩图像尺寸的 resize() 函数默认的插值策略改成了双三次插值:

The default resampling filter has been changed to the high-quality convolution Image.BICUBIC  instead of Image.NEAREST , for the resize()  method and the pad() , scale()  and fit()  functions.

而我原始的代码因为没有考虑这个问题,直接使用了默认参数,所以在新的环境下也就出现了这样“让人出乎意料”的问题。

这样的一次经历,让我对于代码中的默认参数有了一些新的看法。

一点想法

首先就需要问自己一个问题,我们为什么需要“默认参数”?我觉得,归根到底,还是为了方便。

一切的优化、预设,在我看来,都是为了一个目的,“节省时间、珍爱生命”。为了尽可能避免开发者使用这些功能时需要考虑太多,尽可能的“开箱即用”,以将思考的时间用到更加重要的地方去。

但是,这样就将我们对代码可以如期的、稳健的运行的“信任”和“期望”托付给了我们使用的第三方工具的开发者。虽然大多数情况下,我们可以对他们保持足够的信任,但是我们不得不考虑,第三方工具开发者对于所处领域的认识、对于相关知识的掌握程度、对于用户的可能行为的推测,都可能与我们这些使用者的实际情况有所偏差。所以我们真的很难、也不能完全,将我们的代码这辆“货车”的“未来”依附到一个具有着不可控因素的“发动机”上。

所以我们应该怎么做呢?难道真的要放弃默认参数带来的便利么?虽然放弃就在很大程度上可以避免这样的问题(解决问题的一种直接的方法就是直接省掉出现问题的步骤 :<),但是对于我们这些使用者而言,放弃是不可能放弃的。

一些应对策略

一个比较好用的策略就是使用对使用的第三方库进行版本的固定。例如 python 的包管理工具 pip 和 conda 都是可以直接导出当前环境中所有已经安装的包的版本信息的。这也是目前大多数成熟的代码库使用的策略,对于依赖的版本都进行固定。

另外呢,实际上有一点,这里值得提一下,就是这里的 pillow 实际上使用的是Semantic Versioning规则,也就是语义化版本,核心内容如下:

版本格式:主版本号.次版本号.修订号,版本号递增规则如下:

- 主版本号:当你做了不兼容的 API 修改,

- 次版本号:当你做了向下兼容的功能性新增,

- 修订号:当你做了向下兼容的问题修正。

先行版本号及版本编译元数据可以加到“主版本号.次版本号.修订号”的后面,作为延伸。

可见对于我面对的两个版本 7.2.0 和 6.2.1 是分属两个不同的主版本的。二者之间会存在不兼容的 API 的更改。

虽然使用版本固定的策略可以很好的控制依赖的行为,但是有时候我会想要使用更新的版本来获得可能更加强大、完善的、高效的功能。这种想法时常会出现在我的脑海里,但是它真的好么?或者说合理么?这里所谓的“合理”指的是更加适合于我们的任务、实际的工作。

随着敲下的代码越来越多,在 Linux 上工作的时间越来越长,我渐渐明白了,一味地追随高版本、新版本,可能并不是最优的选择。我们的开发的最终的目的是为了程序可以稳定持久的按照预期运行,如非必要,一味地引入过新的版本,带来的增益反而可能会小于因为引入新版本带来的种种问题而造成的不必要的付出。

虽然我的经历可以看做是一种有益的体验,它让我意识到了版本之间的差异带来的问题。但是在那时给我造成的“抓耳挠腮”般的困扰也是让我十分难受和焦虑的 :<。这也算是“痛并快乐着”吧。

除了版本控制之外,还有一个办法,那就是识别出那些更加重要的参数,进行专门的配置。这应该是非常难的,因为它需要我们对于目标任务足够敏感。当然,这可能对于一些简单的函数来说可能会比较容易些,如果特别复杂的函数(一堆参数)可能就会非常困难了。

结语

一个问题,一次总结。

这次的问题让我看到了关注依赖库版本的重要性,同时也提醒了我,一个合格的编码者更要谨慎的对待自己所使用的工具。


发布于: 2020 年 08 月 16 日阅读数: 1024
用户头像

Lart

关注

数不清的问题,数不清的改进 2020.08.16 加入

简单、淳朴的年轻人!

评论

发布
暂无评论
一次由默认参数引起的思考