前几天逛豆瓣读书,看到一本 Python 新书,评价还不错。书名是《从0到1 Python即学即用》,作者莫振杰,人民邮电出版社今年 3 月份出版,其分数达到了令人吃惊的 9.6 分(82 人评价)。
豆瓣链接:book.douban.com/subject/363…
虽然我不是 0 基础的 Python 开发者,但平时也会写一点面向初学者的技术博客,所以对这本书产生了兴趣。想看看这本书有哪些独到之处,希望自己能从优秀作者身上学到点东西。
于是我当天便下单了一本《从0到1》。书在次日晚上送到,在迫不及待地读完大半本后,我发现事情好像有点不太对劲。仅前 14 章,书中便已出现多达几十多个知识或常识性错误,有些错误甚至低级到让人匪夷所思。
我挑选了其中 30 个有代表性的错误摘录在此,邀请大家一起鉴定。
书中部分错误摘录
1. Python 只能拿英文字母做变量名吗?
Python 完全支持用 unicode 作为变量名(从 Python 3 开始),但作者却说只能变量名由英文字母组成。
>>> 世界 = 'World'
>>> print(世界)
World
离谱程度:⭐⭐⭐
2. 三引号真的等于“注释”吗?
在介绍注释时,作者给了一个很直接的观点:“多行注释使用三引号”。
这个说法不说是错的,至少不够严谨。据我说知,主流 Python 项目基本都是直接用 #
号来作为多行注释。而三引号作为创建字符串字面量的一种方式,是可当成注释用,但从未大规模流行过。
离谱程度:⭐⭐
3. Python 没有 switch 语句吗?
一本号称基于 Python 3.11 版本写的书,斩钉截铁地说 Python 没有 switch 语句,我不理解。真的就不提一嘴 match ... case ...
吗?
离谱程度:⭐⭐⭐
4. Python 中没有数组吗?
Python 中当然有数组(只是不常用),作者你为什么不试试执行下 import array
呢?
官方文档:array — Efficient arrays of numeric values — Python 3.11.3 documentation
离谱程度:⭐⭐
5. del
不能删除列表的最后一个元素吗?
在介绍列表的 pop()
方法和 del 语句的区别时,作者说用 del 是无法删除列表的最后一个元素的。我实在不懂他为什么要这么说。
咱先不说对比 pop 和 del 这件事本身就有点离谱,而且,执行 del animals[-1]
不就是删了最后一个元素吗?问题在哪呢?
离谱程度:⭐⭐⭐
6. 列表有 join 方法吗?
在介绍列表的内置方法时,作者把 join()
列在了里面。但是 .join()
从来都是字符串类型的内置方法,而不是列表的。也许作者使用的是来自另一个平行宇宙的 Python?
离谱程度:⭐⭐⭐⭐⭐
7. 将空列表赋值给变量就能清空列表吗?
在介绍如何清空列表时,作者提到将一个空列表赋值给原变量,就能达到“清空”列表的目的。
看到这个说法,我有些啼笑皆非。赋值操作只是修改了原变量的绑定关系(binding)而已,清空列表从何谈起?
离谱程度:⭐⭐⭐⭐⭐
8. 列表只能和正整数相乘?
不是这样的,列表可以和任何整数相乘:
>>> nums = [1, 2, 3]
>>> nums * 0
[]
>>> nums * -1
[]
离谱程度:⭐⭐⭐
9. 元组是由小括号定义的?
不,小括号不能定义元组,逗号才是定义元组的关键。
>>> t = 1, 2
>>> type(t)
<class 'tuple'>
离谱程度:⭐⭐⭐⭐⭐
10. 代码示例中,和 None 的对比不规范
在一份代码示例中,作者写出了 if result == None
这种代码。但是,稍有经验的 Python 开发者都知道,这类判断得写成 if result is None
吧?
小声问一句:作者清楚 is
和 ==
的区别吗?
离谱程度:⭐⭐⭐⭐⭐
11. 字符串替换默认只替换一次吗?
在介绍字符串的 .replace()
方法时,作者说该方法默认只会替换一次,其中 n
的默认值是 1。
这个错误实在离谱。哪怕稍微写过一点 Python,都应该知道 .replace()
默认替换无数次啊?n
(其实参数名不叫 n
而叫 count
) 的默认值是 -1
,不是 1
。
replace(old, new, count=-1, /) method of builtins.str instance
Return a copy with all occurrences of substring old replaced by new.
离谱程度:⭐⭐⭐⭐⭐
12. 介绍字符串 split() 方法时缺少 maxsplit 参数
紧接着,在 .replace()
之后,介绍 .split()
方法时,作者好像突然忘了 .split()
也是支持“最大切分次数(maxsplit
)” 参数的。整段介绍只字未提该参数,实在奇怪。
离谱程度:⭐⭐⭐⭐
13. split() 的默认切分策略不是空格
当调用 split()
时不提供任何参数时,Python 不是把空格作为分隔符,而是把包括制表符、换行符等在内的所有空白字符当做分隔符。
离谱程度:⭐⭐⭐⭐⭐
14. strip() 支持多个字符作为参数,而不是一个
介绍 strip() 方法时,作者说该方法的作用是去除首尾的字符,这没错。但在介绍细节时,作者在“语法”和“代码示例”中只写了单个字符 char。
这是错误的,strip()
默认支持多个字符:
>>> s = 'Hello, World'
>>> s.strip('eHd')
'llo, Worl'
离谱程度:⭐⭐⭐⭐⭐
15. 字典是无序的吗?
我很确定,至少在作者所声称的这本书所基于的 Python 3.11 版本中,字典是有序(保留插入顺序)的,而不是无序的。
离谱程度:⭐⭐⭐⭐⭐
16. print 一个集合会对其进行排序吗?
在介绍集合类型时,作者提出了一个骇人听闻的说法:“当我们用 print() 输出一个集合时,会对其进行排序。”我想破了脑袋,也想不出他为啥要这么说,因为众所周知,Python 中的集合是无序的啊?
直到我仔细研读这段话的前面内容,才隐约猜出他为什么会有这个观点:
原来是他在打印 {1, 2, .., 5}
这个集合时,碰巧看到了这个所谓的“排序”现象,将这种由于哈希值导致的巧合当成了 Python 的固定行为。
我真的不知道该说什么好。
离谱程度:⭐⭐⭐⭐⭐
17. 谁会写出 num in result.keys()
这样的代码?
看到这段代码时,我沉默了。我已经很久没有看到有人写出这样的判断语句了。
众所周知,在 Python 中,判断某个键是否在字典中,更常见的写法是这样:
if num not in result:
...
离谱程度:⭐⭐⭐
18. 函数分为“有返回值”和“无返回值“两种?
在介绍函数时,作者说函数分为“有无返回值”两种。恕我才疏学浅,这种分类方式我是第一次听到,非常具有原创性。
不知道作者怎么看待生成器函数(协程)?你准备给它归入哪一类?
离谱程度:⭐⭐
19. 介绍传参报错时的逻辑谬误
在介绍函数传参时,作者写了一个会报错的示例:”有默认值的参数在前“。其中,他对于报错原因的解释令人非常费解。
解释器已经明说了,这是一个语法错误(SyntaxError)。这个语法错误在定义 ball
函数时就已经抛出,而不是下面的 ball('red')
调用语句。
但作者怎么还扯什么“第二参数没有传入值”之类的原因呢?他真的有认真阅读错误栈信息吗?
离谱程度:⭐⭐⭐⭐⭐
20. 介绍 sum() 函数时的奇怪逻辑
在介绍 max()、min() 和 sum() 函数时,作者非常“贴心”地特意提了一句:max() 和 min() 支持 max(1, 2, 3)
这种所谓的支持”一组数“的调用方式,而 sum()
不支持。
这本身是事实,但作者并没有详细解释原因,而只是抛出一句语焉不详的”你可以自行试一下“。
这到底有啥好试的?sum
不支持这种调用,是因为函数签名本来就不支持可变参数好嘛??
max() 函数签名:
max(...)
max(iterable, *[, default=obj, key=func]) -> value
max(arg1, arg2, *args, *[, key=func]) -> value
再看 sum() 函数签名:
sum(iterable, /, start=0)
Return the sum of a 'start' value (default: 0) plus an iterable of numbers
其中根本就没有支持可变参数啊。
离谱程度:⭐⭐
21. __init__()
方法的第一个参数不是必须叫 self
self
这个名字是一个事实标准,但不是不能打破的约束。
离谱程度:⭐⭐⭐
22. 通过实例来访问类属性真的不规范吗?
作者说,在“实际开发”(这个词在本书中多次出现)中,不建议通过实例来访问类属性,这不规范。
再次恕我才疏学浅,这个说法我又是第一次听到。通过实例访问类属性实在常见,我不知道作者为什么这么说。
离谱程度:⭐⭐⭐
23. 异常捕获代码有 bug
在演示 try ... finally
语句时,作者给出了上面的代码。
这段代码中有一个很常见的 bug:file = open(...)
应该在 try
之外,而不是在 try
内部。像现在这样写,当文件无法正常打开时,finally
内的代码块会报出 file
变量未定义的错误。
正确的写法:
file = open(...)
try:
# Do something with file
finally:
file.close()
离谱程度:⭐⭐⭐⭐
24. 应该优先使用 readlines 读取文件?
在文件读取部分,作者抛出了一个观点:实际开发(天啊,又一次所谓的”实际开发“)中,应该优先使用 readlines()
而不是 readline()
,因为后者一次只能读取一行……
再次恕我没啥见识,这是我第一次听到类似说法。
作者为何要对 Python 中的事实标准做法 for line in f:
视而不见呢?要知道,它也是一次只读一行哦。
离谱程度:⭐⭐⭐⭐
25. 尽量少用正则表达式的 search 和 match?
又一个骇人听闻的”实际开发“中的建议。
离谱程度:⭐⭐⭐⭐
26. 翻转字符串必须转换成列表吗?
请听我说,不用搞一个列表这么麻烦,你只要写 ''.join(reverse(s))
就行了。
离谱程度:⭐⭐
27. 关于深拷贝和浅拷贝
上面这段文字里几乎全是错误。
- 使用
{**persion}
这种解包方式,完成的是浅拷贝,而不是作者所声称的深拷贝,不信的话,给字典塞一个列表进去试试就知道了 - 在执行浅拷贝时,Python 根本不区分什么”基本类型“或”引用类型“(多么罕见的 Python 术语!),浅拷贝总是只拷贝引用(视值的可变性,效果不同),只有深拷贝才会递归地复制值
离谱程度:⭐⭐⭐⭐⭐
28. 代码语法错误
离谱程度:⭐⭐
29. 不规范的类型判断
Python 中,类型判断应该优先使用 isinstance()
,而不是 type(...) == str
。
离谱程度:⭐⭐
30. 关于 filter()
的解释非常怪
我不知道作者写这么一大串,说 filter(None)
的结果不符合预期是啥意思。None
是 filter 的参数缺省值,不存在什么应该是 [False, None ...]
满足条件的说法,这不是什么所谓的”特例。“
离谱程度:⭐⭐⭐
后记
读完《从0到1》的前半部分,我合上书,看着封面上的宣传文案:“‘六边形’Python教程,满足初学者的学习幻想!”,感受到了一种黑色幽默。忽然很心痛那笔购书款——整整 86.3 元,足够我喝一周的奶茶了。
当然,咱也不是说一点收获没有。我复盘了一下,分析自己为什么会买这本奇书:
- 评分高:豆瓣评分 9.6,排名靠前,大量有文字的评价很真实,不像刷的(我太天真了)
- 对出版社有好感:人民邮电出版社图灵公司出版,之前读过不少他们出版的 Python 书,印象非常好,没想到这次翻车翻到姥姥家去了…
我买过不少糟糕的技术图书,但这次阅读《从0到1》的过程,大大刷新了我对于“一本书到底有多烂”的认知。真不知道,这种错误百出的书怎么能出版,咱就是说,但凡找个有几年经验的 Python 开发过一遍,也不至于这样啊?
突然又想到,既然书写成这样也能出版,那自己是不是也能出书啊?至少我能做到不跟读者说:“join 是列表的内置方法”“字符串替换默认替换 1 次”这种天方夜谭。
文章较长,权当给大家买书排个雷,希望各位学习 Python 的同学避开此书,祝大家周末愉快。