本文原创并首发于公众号【Python猫】,未经授权,请勿转载。

原文地址:https://mp.weixin.qq.com/s/NuzfuH_zCZzcrmSFR04NHw

(一)

上周,我翻译了一篇文章,解释了为什么 Python 3 把 print 改为函数? 概括有如下几点原因:1、print 不适宜作为应用程序级的语句。2、改为一个函数,可以实现更复杂的功能。3、改为一个函数,能方便地进行替换。

在 Python 2 中,print 是个语句(statement),它的级别就跟 for、if、def 等关键字相同,这是一个古老的设计(毕竟 Python 诞生于 1989 年),改成 print() 函数,意味着它升级了。

在查阅资料的时候,我发现 print 在历代版本中,一直发展变化,到了今天,它自身已足够完善了,可是外部的挑战一直不断。

因此,这篇文章再来聊聊它:介绍 print 的现状,追溯它的历史,说说它的挑战者,挖挖那些更加本质的东西。

(二)

在 3.0 版本中,print() 函数全新登场,开发者可以自定义打印对象的间隔(默认是空格)、终止方式(默认是换行)、以及输出位置(默认是标准输出 sys.stdout)。

而到了 3.3 版本,它还添加了一个新的参数,可以决定是否要刷新数据流。

至此,这个函数的完整格式就变成了 print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) ,与升级前的 print 语句是天壤之别啦。

优点是显而易见的,可定制的参数带来了使用场景的扩充。

(三)

其实,在这次大版本的改动之前,早期的 print 语句并非是一成不变的,核心开发者们一直在完善它。

例如,在 2000 年的 PEP-214 之前,print 语句只能用于标准输出(sys.stdout),只有当提出这个提案后,print 才可以打印内容到类文件对象(file-like object)中。

(注:PEP 即 Python 改进提案,更多介绍详见旧文《学习Python,怎能不懂点PEP呢?》)

这次调整后,它的写法可以如下(其中,mylogfile 是用于记录打印信息的文件路径):

print >> mylogfile, 'this message goes to my log file'

在只接触过 Python 3 的同学眼里,这个写法可能很别扭吧,其实它等同于如今的:

print('this message goes to my log file', file = mylogfile)

(四)

上例是一次成功的改进,但有趣的是,社区内也有一次失败的修改提案。

与 print() 函数相同,print 语句在打印完一个对象后,默认会换行,因此,当打印的内容自带了换行符的时候,最终的打印结果就会出现一个多余的换行。

2001 年的时候,有开发者在 PEP-259 中提议,根据打印的最后一个字符的类型,设置几个标志位,以此决定是否要默认换行。校验规则如下:

  • -1 ——如果最后一个对象是以换行符结束的字符串
  • 0 ——如果最后一个对象是以空白字符结尾的字符串,既不是空格也不是换行符
  • 1 ——在所有其它情况下(包括最后一个对象是空字符串或不是字符串的情况)

根据这些规则,print 语句遇到 -1 标志位的时候,就不再做默认的换行了,似乎可以解决多余换行的问题。

然而,这个提案被否决了。反对的意见主要是:这样可能会破坏掉无数个 CGI 脚本,而且 Python 中已经有太多的“魔法”了。

这一套规则确实太神奇了,幸好没有实施。在当前的版本中,只需调整 end 参数,就可以避免多余换行的问题。

(五)

阅读过往的 PEP 文档,就是在阅读 Python 的历史,从中你可以看到设计者们对功能细节的打磨过程,最终你就明白了,Python 是如何一步一步地发展成今天的样子。

不过,历史中除了能看到精华,也可以看到一些包袱。print() 函数的升级就是在甩掉包袱,前不久我写了《聊聊 Python 的内置电池》,聊到了 Python 中废弃部分标准库的话题,也是一个很好的观察例子。

除此之外,“print”的命名本身也算是一种包袱。

早期的计算机使用纸带作为信息载体,程序的运算结果需要 print 在纸带上,所以顺理成章地,有些编程语言就使用了“print”来表示程序的输出操作。尽管后来不再使用纸带了,一些语言仍然延用这个词,例如 C 语言以及借鉴了 C 语言的 Python。

Python 的另一个借鉴对象是 Shell,这是一种古老的脚本语言,可它没有“print”的包袱,它用的是 echo。这个词的本意是回声,后来也指雷达的回波,被用于计算机编程中,则又被赋予了“应答、回显”之义,更直白的表述应该是“输出、打印”。

Python 从 C 中借用了“print”命名,又从 Shell 中借用语句式的表达,形成了自己 print 语句,如今到了新的版本,它去除了语句式的表达,却仍保留着原始命名,可以说这个包袱是永远脱不掉了。

但是,话说回来,词语在演化过程中会获得新的生命,它的意义全在于如何使用。所以,虽然没有了纸带这个物理载体,print 这个词却“改头换面”地活了下来。

它还拥有很多的表兄弟姐妹呢,非常热闹(试试你能认出几个?):

print("点个赞吧!")
printf("点个赞吧!");
print_r('点个赞吧!');
var_dump('点个赞吧!');
NSLog(@"点个赞吧!");
System.out.println("点个赞吧!");
console.log("点个赞吧!");
cout << "点个赞吧!" << endl;
Console.WriteLine("点个赞吧!");
writeln('点个赞吧!')
fmt.Println("点个赞吧!");
Response.Write("点个赞吧!");
alert("点个赞吧!")
echo "点个赞吧!"
puts "点个赞吧!"
say "点个赞吧!";

(六)

语言内部的发展历史,以及不同语言的相似表述,都表明着一件事,那就是打印操作很重要,而且我们对它的要求还很复杂多样。

Python 中的 print 语句能发展成今天的 print() 函数,已经非常完善了。

不过,需求是无止境的,作为最常用的调试手段,print() 还达不到十全十美。它的好处是简单直白、容易上手,但缺点则是功能单一、效率较低,在需要定制格式的频繁使用场景下,不堪大用。

这在不同编程语言中是通病,因此大家都默契地提供了用于调试的日志模块,例如 Java 的 log4j,C++ 的 log4cxx,当然还有 Python 的 logging。

日志模块 logging 可以说是对 print() 函数的替代式升级,主要优点是更加灵活高效,例如可以设置不同的日志等级、配置多样的格式化信息、甚至可以输出日志到远程服务器上。

当然,日志模块只是一种解决方案,也并不是最完美的。

在 Python 中还有一些模块可以用于调试,例如最主流的 pdb,它可以设置断点、分步调试、查看栈片段、动态调值等,用得好,有奇效。主流的 IDE 工具也都提供了一些调试手段,相比于简单的 print(),它们具有降维打击的优势。

今年 4 月,Github 上开源了一个专用于调试程序的库,名叫 PySnooper ,短短两个月,它就收获了近 12K 个关注。这个三方库的口号是“Never use print for debugging again”,其目标就是在调试代码时完全替代 print。

这个库的用法非常简单,只需一行代码,就可以实现对整个函数的监听,做到记录每一行的执行时间、记录每个变量的赋值等等,而且还可以使用“with”语句,监听部分的代码块,或者使用“watch”命令,专门监听特定的变量值。

这个库强大而惊艳,除了上述作用,它还能监听指定格式开头的代码,能在多线程中监听线程,甚至支持用户自定义的监听规则。难怪它一经面世,就好评如潮,人人奔走相告。

snoop 这个单词很有意思,它指的是嗅探、窥探和监听。首字母大写的 Snoop ,译作史努比,则是一只被很多人喜爱的漫画小狗。所以这个 PySnooper 库就令我不由地产生了一种联想:它是一只嗅觉异常敏锐的小狗,明白无误地为你执行各种监听任务。

(七)

最后,我们可以来回顾一下 print 的发展历史了,有两条线索,一条是它自身发展的明线,另一条是它的挑战者们的暗线。

先看明线吧,早期版本的 print 语句带有 C 和 Shell 的影子,它是个应用程序级的 statement,使用十几年间,有过一些改进的尝试,例如 PEP-214 和 PEP-259;到了 2009 年的大版本 3.0,Python 把 print 语句改成了 print() 函数,使它成为了众多内置函数的一员,随后在 3.3 版本,又对它做了一次功能增强,至此,它完成了自己的华丽蜕变,占据了稳固的一席之地。

至于暗线,print 的竞争对手们可谓众多,像传统的日志模块 logging、调试模块 pdb、以及主流 IDE 的调试功能,等等,如今还有一位后起之秀 PySnooper,无不瞄准了 print 的位置,摩拳擦掌,虎视眈眈。

print 一词最早应该跟纸带相关,用途和需求场景都很少,如今的计算机世界已经不可同日而语,所以才促进了 print 自身的发展,也刺激了众多对手们的崛起。

print 代表了一种诉求/思想:输出计算结果、记录程序过程、监察对象变化,然后用于查看、分析、调试、展示等等。

明线上的发展,就是继承了它的名字,壮大 print;暗线上的发展,则是继承了它的思想,为了实现目的,各施手段,百花齐放。

print 当然不是 Python 所特有的,这明暗两线的发展也同理,如果你把视野放到任何一个经得起时间考验的语言上,必然也会看到相似的发展历程与竞争故事。

最后,如果你想了解更多内容,可通过以下链接查看:

https://docs.python.org/3/library/functions.html#print

学习Python,怎能不懂点PEP呢?

https://www.python.org/dev/peps/pep-0214/

https://www.python.org/dev/peps/pep-0259/

为什么 Python 3 把 print 改为函数?

https://github.com/cool-RR/PySnooper

公众号【Python猫】, 本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。

聊聊 print 的前世今生的更多相关文章

  1. 聊聊CMDB的前世今生

    CMDB,Configuration Management DataBase,配置管理数据库,是与 IT 系统所有组件相关的信息库,它包含 IT 基础架构配置项的详细信息. 传统运维思路下的CMDB, ...

  2. 我的 2019 年 Python 文章榜单

    现在是 2020 年的第一天,我相信从昨天开始,各位的信息流里肯定充斥了各式各样的年度盘点/回顾/总结/记录之类的内容.虽然来得稍晚了,但我还是想给诸位送上这一篇文章. 我将在本文中列出自己于 201 ...

  3. Python 如何移除旧的版本特性,如何迎接新的特性?

    2020 年 4 月 20 日,Python 2 的最后一个版本 2.7.18 发布了,这意味着 Python 2 是真正的 EOL(end of life)了,一个时代终于落幕了. Python 2 ...

  4. python学习——练习题(10)

    """ 题目:暂停一秒输出,并格式化当前时间. """ import sys import time def answer1(): &quo ...

  5. 混合事务分析处理“HTAP”的技术要点分析

    HTAP是近些年来比较火的一个概念,本文将聊聊HTAP的前世今生及技术特点. 一.数据应用类别 根据数据的使用特征,可简单做如下划分.在选择技术平台之前,我们需要做好这样的定位. 1.1 OLTP 联 ...

  6. 为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生【宇哥带你玩转MySQL 索引篇(二)】

    为什么MySQL要用B+树?聊聊B+树与硬盘的前世今生 在上一节,我们聊到数据库为了让我们的查询加速,通过索引方式对数据进行冗余并排序,这样我们在使用时就可以在排好序的数据里进行快速的二分查找,使得查 ...

  7. 23 | 知其然知其所以然:聊聊API自动化测试框架的前世今生

  8. Apache ServiceComb 开源两周年,聊聊其与微服务的前世今生

    欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...

  9. [转帖]来聊聊,华为与H3C(华三)的前世今生!

    本篇,是以真实事件改编,将以故事篇的方式呈现出来. 本故事将分为两个篇幅讲述. 在中国的网络通信设备市场,有两个华字辈的选手,一名叫“华为技术有限公司”,另一名叫“杭州华三通信技术有限公司”. 这两个 ...

随机推荐

  1. CentOS7中安装MariaDB

    什么是mariaDB? 在线安装(慢的要命) RPM离线安装(CentOS7.X) 在线安装 打开官方网站 https://mariadb.org/ 点击Download,跳转到下一页面 继续点击Do ...

  2. Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享

    Java生鲜电商平台-生鲜售后系统的退款架构设计与代码分享 说明:任何一个电商行业都涉及到退货与退款的问题,但是生鲜电商行业还设有一个显著的特点,那就是换货.在人性面前,各种各样的退货,退款,换货的售 ...

  3. 获取Zabbix 中资源的使用率

    import pymysql as MySQLdb import time import datetime import xlsxwriter # zabbix数据库信息: zdbhost = 'xx ...

  4. cmd for install pygame in python 3.7

    Higher version Python better and convinient to use! Down load pygame whl file: C:\Work\software>p ...

  5. 相关性不一定等于因果性:从 Yule-Simpson’s Paradox 讲起

    1. 两件事伴随发生,不代表他们之间有因果关系 - 从一些荒诞相关性案例说起 在日常生活和数据分析中,我们可以得到大量相关性的结论,例如: 输入X变量,有98%置信度得到Y变量 只要努力,就能成功 只 ...

  6. input监听

    <h1> 实时监测input中值的变化 </h1> <input type="text" id="username" autoco ...

  7. Qt之高DPI显示器(一) - 解决方案整理

    目录 DPI发展 1.PPI 2.DPI 一.Win自适应系统 二.Qt机制 1.Windows系统DWM缩放 2. Qt适配高DPI 3.适配DPI结论 三.Qt适配 四.自己适配 1.窗口大小 2 ...

  8. mac mysql start ERROR! The server quit without updating PID file

    在mac下安装完mysql,启动时出现error: ERROR! The server quit without updating PID file (/usr/local/var/mysql/nal ...

  9. 机器学习-Python 01

    机器学习中最常用最流行的语言工具现阶段应该是Python, 这篇文章主要介绍一些常用的Python语法知识.本篇博文适合那些有其他语言基础的程序员们,如果一点基础都没有,我建议先跳过.博主以前是做移动 ...

  10. 学习ThinkPHP的第20天--MySQL事务操作、查询事件、一对一关联

    之所以从20天才开始写随笔记是因为之前没搞自己的博客 从20天开始记录我在ThinkPHP中的点点滴滴 1.MySQL事务操作 /**事务操作*/ //startTrans启动事务.rollback回 ...