我买了本豆瓣 9.6 分的 Python 书,发现每 5 页一个错误?!

△点击上方“Python猫”关注 ,回复“1”领取电子书

dbf420c7cbef28e07d336e9d296a1f81.png

大家好,我是猫哥。今天分享一篇文章,你可以当成娱乐文看,也可以从中学到很多!文中的某些话题,我曾写过相应的文章,因此添加了部分注释。

作者:TheFry

链接:https://juejin.cn/post/7240248679515963451

版权:此处已获得作者授权转载。如需转载,请联系原作者。

前几天逛豆瓣读书,看到一本 Python 新书,评价还不错。书名是《从0到1 Python即学即用》,作者莫振杰,人民邮电出版社今年 3 月份出版,其分数达到了令人吃惊的 9.6 分(82 人评价)。

a99b1556c4763b74bc4b4b457e52cf80.jpeg

豆瓣链接:https://book.douban.com/subject/36342454/

虽然我不是 0 基础的 Python 开发者,但平时也会写一点面向初学者的技术博客,所以对这本书产生了兴趣。想看看这本书有哪些独到之处,希望自己能从优秀作者身上学到点东西。

于是我当天便下单了一本《从0到1》。书在次日晚上送到,在迫不及待地读完大半本后,我发现事情好像有点不太对劲。仅前 14 章,书中便已出现多达几十多个知识或常识性错误,有些错误甚至低级到让人匪夷所思。

我挑选了其中 30 个有代表性的错误摘录在此,邀请大家一起鉴定。

书中部分错误摘录

1. Python 只能拿英文字母做变量名吗?

259c82ee3e39a9123bc65a238efe682f.jpeg

Python 完全支持用 unicode 作为变量名(从 Python 3 开始),但作者却说只能变量名由英文字母组成。

>>> 世界 = 'World'
>>> print(世界)
World

离谱程度:⭐⭐⭐

猫哥注:这是很常见的错误,若原书是英语的话,似乎情有可原,我曾写过《醒醒!Python已经支持中文变量名啦!》,里面就是两本非常经典的英文书。

2. 三引号真的等于“注释”吗?

01bf634aa7de3535bb4888974ab55bc9.jpeg

在介绍注释时,作者给了一个很直接的观点:“多行注释使用三引号”。

这个说法不说是错的,至少不够严谨。据我说知,主流 Python 项目基本都是直接用 # 号来作为多行注释。而三引号作为创建字符串字面量的一种方式,是可当成注释用,但从未大规模流行过。

离谱程度:⭐⭐

猫哥注:三引号作为注释符是挺奇怪的,但不可否认的是,Python 之父 Guido 却曾经推荐过这种写法,参见《Python 为什么用 # 号作注释符?

3. Python 没有 switch 语句吗?

8af88ffe8d7f05fa898e53cfb4ae4ad4.jpeg

一本号称基于 Python 3.11 版本写的书,斩钉截铁地说 Python 没有 switch 语句,我不理解。真的就不提一嘴 match ... case ... 吗?

离谱程度:⭐⭐⭐

猫哥注:Python 确实没有 switch 语句,为什么不设计它呢?参见《Python 为什么不支持 switch 语句?

4. Python 中没有数组吗?

36ea7510d6339d43058ec175bf58245d.jpeg

Python 中当然有数组(只是不常用),作者你为什么不试试执行下 import array 呢?

官方文档:array — Efficient arrays of numeric values — Python 3.11.3 documentation

离谱程度:⭐⭐

5. del 不能删除列表的最后一个元素吗?

e25a5f08ae6ec4dfbf1e7033a135f11f.jpeg

在介绍列表的 pop() 方法和 del 语句的区别时,作者说用 del 是无法删除列表的最后一个元素的。我实在不懂他为什么要这么说。

咱先不说对比 pop 和 del 这件事本身就有点离谱,而且,执行 del animals[-1] 不就是删了最后一个元素吗?问题在哪呢?

离谱程度:⭐⭐⭐

6. 列表有 join 方法吗?

51c89379e4c1adfd62a6142669feea24.jpeg

在介绍列表的内置方法时,作者把 join() 列在了里面。但是 .join() 从来都是字符串类型的内置方法,而不是列表的。也许作者使用的是来自另一个平行宇宙的 Python?

离谱程度:⭐⭐⭐⭐⭐

7. 将空列表赋值给变量就能清空列表吗?

d49813c9bcabc21d68044b090f323165.jpeg

在介绍如何清空列表时,作者提到将一个空列表赋值给原变量,就能达到“清空”列表的目的。

看到这个说法,我有些啼笑皆非。赋值操作只是修改了原变量的绑定关系(binding)而已,清空列表从何谈起?

离谱程度:⭐⭐⭐⭐⭐

8. 列表只能和正整数相乘?

75b27016471a80722617d39a72c3fab5.jpeg

不是这样的,列表可以和任何整数相乘:

>>> nums = [1, 2, 3]
>>> nums * 0
[]
>>> nums * -1
[]

离谱程度:⭐⭐⭐

9. 元组是由小括号定义的?

b83bbc76bcc680222ca2be069abf4498.jpeg

不,小括号不能定义元组,逗号才是定义元组的关键。

>>> t = 1, 2
>>> type(t)
<class 'tuple'>

离谱程度:⭐⭐⭐⭐⭐

猫哥注:这也算是典型的错误吧,推荐大家翻看《流畅的Python》关于元组的章节。

10. 代码示例中,和 None 的对比不规范

c2daec28a5b04b3b4ac40ffdd909a2b4.jpeg

在一份代码示例中,作者写出了 if result == None 这种代码。但是,稍有经验的 Python 开发者都知道,这类判断得写成 if result is None 吧?

小声问一句:作者清楚 is== 的区别吗?

离谱程度:⭐⭐⭐⭐⭐

11. 字符串替换默认只替换一次吗?

585aed3c3a48e8f80530fed2c63892d7.jpeg

在介绍字符串的 .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 参数

338c7fc4622cebf9022afa2239f142b4.jpeg

紧接着,在 .replace() 之后,介绍 .split() 方法时,作者好像突然忘了 .split() 也是支持“最大切分次数(maxsplit)” 参数的。整段介绍只字未提该参数,实在奇怪。

离谱程度:⭐⭐⭐⭐

13. split() 的默认切分策略不是空格

a4c2a220feec1aeb3d2757552bff017c.jpeg
img

当调用 split() 时不提供任何参数时,Python 不是把空格作为分隔符,而是把包括制表符、换行符等在内的所有空白字符当做分隔符。

离谱程度:⭐⭐⭐⭐⭐

14. strip() 支持多个字符作为参数,而不是一个

e563397cbf0e6c65133b70eedb4fbaa9.jpeg

介绍 strip() 方法时,作者说该方法的作用是去除首尾的字符,这没错。但在介绍细节时,作者在“语法”和“代码示例”中只写了单个字符 char。

这是错误的,strip() 默认支持多个字符:

>>> s = 'Hello, World'
>>> s.strip('eHd')
'llo, Worl'

离谱程度:⭐⭐⭐⭐⭐

15. 字典是无序的吗?

61a4a69d6eaf0489e6b276f30ea27349.jpeg

我很确定,至少在作者所声称的这本书所基于的 Python 3.11 版本中,字典是有序(保留插入顺序)的,而不是无序的。

离谱程度:⭐⭐⭐⭐⭐

16. print 一个集合会对其进行排序吗?

86fb3a624df82b3cedb9af1ebfde64ca.jpeg

在介绍集合类型时,作者提出了一个骇人听闻的说法:“当我们用 print() 输出一个集合时,会对其进行排序。”我想破了脑袋,也想不出他为啥要这么说,因为众所周知,Python 中的集合是无序的啊?

直到我仔细研读这段话的前面内容,才隐约猜出他为什么会有这个观点:

5842f54724727f4b4fcc32133f217eaf.jpeg

原来是他在打印 {1, 2, .., 5} 这个集合时,碰巧看到了这个所谓的“排序”现象,将这种由于哈希值导致的巧合当成了 Python 的固定行为。

我真的不知道该说什么好。

离谱程度:⭐⭐⭐⭐⭐

17. 谁会写出 num in result.keys() 这样的代码?

26ebd1c96e4bab11d7907d2a7c36fc87.jpeg

看到这段代码时,我沉默了。我已经很久没有看到有人写出这样的判断语句了。

众所周知,在 Python 中,判断某个键是否在字典中,更常见的写法是这样:

if num not in result:
    ...

离谱程度:⭐⭐⭐

18. 函数分为“有返回值”和“无返回值“两种?

602ea4c858b34836084c654233c6e5d3.jpeg

在介绍函数时,作者说函数分为“有无返回值”两种。恕我才疏学浅,这种分类方式我是第一次听到,非常具有原创性。

不知道作者怎么看待生成器函数(协程)?你准备给它归入哪一类?

离谱程度:⭐⭐

猫哥注:准确地说,Python 有“写返回值”与“不写返回值”两种,但是绝对不存在“没有返回值的函数”,因为它所有的函数都有返回值!参见《Python 函数为什么会默认返回 None?

19. 介绍传参报错时的逻辑谬误

ceec4add5e8cc8072b730fa05a039228.jpeg

在介绍函数传参时,作者写了一个会报错的示例:”有默认值的参数在前“。其中,他对于报错原因的解释令人非常费解。

解释器已经明说了,这是一个语法错误(SyntaxError)。这个语法错误在定义 ball 函数时就已经抛出,而不是下面的 ball('red') 调用语句。

但作者怎么还扯什么“第二参数没有传入值”之类的原因呢?他真的有认真阅读错误栈信息吗?

离谱程度:⭐⭐⭐⭐⭐

20. 介绍 sum() 函数时的奇怪逻辑

6ea38ceadd10307bd8db8b3e3376e986.jpeg

在介绍 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

7ba56ba6f056ba7b98bef7306a724cea.jpeg

self 这个名字是一个事实标准,但不是不能打破的约束。

离谱程度:⭐⭐⭐

22. 通过实例来访问类属性真的不规范吗?

71ca35168753cca5506312e26c37d5ab.jpeg

作者说,在“实际开发”(这个词在本书中多次出现)中,不建议通过实例来访问类属性,这不规范。

再次恕我才疏学浅,这个说法我又是第一次听到。通过实例访问类属性实在常见,我不知道作者为什么这么说。

离谱程度:⭐⭐⭐

23. 异常捕获代码有 bug

e19c86eee66ec6a89f9bd69130d158d9.jpeg

在演示 try ... finally 语句时,作者给出了上面的代码。

这段代码中有一个很常见的 bug:file = open(...) 应该在 try 之外,而不是在 try 内部。像现在这样写,当文件无法正常打开时,finally 内的代码块会报出 file 变量未定义的错误。

正确的写法:

file = open(...)
try:
 # Do something with file
finally:
 file.close()

离谱程度:⭐⭐⭐⭐

24. 应该优先使用 readlines 读取文件?

bfe6d9858e441ef78d33061140d1e0f5.jpeg

在文件读取部分,作者抛出了一个观点:实际开发(天啊,又一次所谓的”实际开发“)中,应该优先使用 readlines() 而不是 readline(),因为后者一次只能读取一行……

再次恕我没啥见识,这是我第一次听到类似说法。

作者为何要对 Python 中的事实标准做法 for line in f: 视而不见呢?要知道,它也是一次只读一行哦。

离谱程度:⭐⭐⭐⭐

25. 尽量少用正则表达式的 search 和 match?

d929b1245d7ff579365f9ee84ba59de6.jpeg

又一个骇人听闻的”实际开发“中的建议。

离谱程度:⭐⭐⭐⭐

26. 翻转字符串必须转换成列表吗?

ea02e010a1c2ce5ee1cc8166efcd7145.jpeg

请听我说,不用搞一个列表这么麻烦,你只要写 ''.join(reverse(s)) 就行了。

离谱程度:⭐⭐

27. 关于深拷贝和浅拷贝

72bd3f834c19874ee0a03ff57fe2e803.jpeg

上面这段文字里几乎全是错误。

  • 使用 {**persion} 这种解包方式,完成的是浅拷贝,而不是作者所声称的深拷贝,不信的话,给字典塞一个列表进去试试就知道了

  • 在执行浅拷贝时,Python 根本不区分什么”基本类型“或”引用类型“(多么罕见的 Python 术语!),浅拷贝总是只拷贝引用(视值的可变性,效果不同),只有深拷贝才会递归地复制值

离谱程度:⭐⭐⭐⭐⭐

28. 代码语法错误

1924ded25be47be16990776b427a5fc3.jpeg

离谱程度:⭐⭐

29. 不规范的类型判断

66dfdb3cd89391ae70f4286edd7aae60.jpeg

Python 中,类型判断应该优先使用 isinstance(),而不是 type(...) == str

离谱程度:⭐⭐

30. 关于 filter() 的解释非常怪

9782e98a2e47e658bb6f6f2bb3aa0a49.jpeg

我不知道作者写这么一大串,说 filter(None) 的结果不符合预期是啥意思。None 是 filter 的参数缺省值,不存在什么应该是 [False, None ...] 满足条件的说法,这不是什么所谓的”特例。“

离谱程度:⭐⭐⭐

后记

读完《从0到1》的前半部分,我合上书,看着封面上的宣传文案:“‘六边形’Python教程,满足初学者的学习幻想!”,感受到了一种黑色幽默。忽然很心痛那笔购书款——整整 86.3 元,足够我喝一周的奶茶了。

当然,咱也不是说一点收获没有。我复盘了一下,分析自己为什么会买这本奇书:

  • 评分高:豆瓣评分 9.6,排名靠前,大量有文字的评价很真实,不像刷的(我太天真了)

  • 对出版社有好感:人民邮电出版社图灵公司出版,之前读过不少他们出版的 Python 书,印象非常好,没想到这次翻车翻到姥姥家去了…

我买过不少糟糕的技术图书,但这次阅读《从0到1》的过程,大大刷新了我对于“一本书到底有多烂”的认知。真不知道,这种错误百出的书怎么能出版,咱就是说,但凡找个有几年经验的 Python 开发过一遍,也不至于这样啊?

突然又想到,既然书写成这样也能出版,那自己是不是也能出书啊?至少我能做到不跟读者说:“join 是列表的内置方法”、“字符串替换默认替换 1 次”这种天方夜谭。

文章较长,权当给大家买书排个雷,希望各位学习 Python 的同学避开此书,祝大家心情愉快。

dbd751cd396522a53fc2fcbb49adb162.gif

Python猫技术交流群开放啦!群里既有国内一二线大厂在职员工,也有国内外高校在读学生,既有十多年码龄的编程老鸟,也有中小学刚刚入门的新人,学习氛围良好!想入群的同学,请在公号内回复『交流群』,获取猫哥的微信(谢绝广告党,非诚勿扰!)~

还不过瘾?试试它们

Python潮流周刊#1:如何系统地自学Python?

Rye:一个实验性质的Python包管理系统

Python 项目工程化最佳实践指南

Python 官方研讨会:彻底移除 GIL 真的可行么?

Python进阶:探秘描述符的工作原理

为什么继承 Python 内置类型会出问题?!

如果你觉得本文有帮助

请慷慨分享点赞,感谢啦

猜你喜欢

转载自blog.csdn.net/chinesehuazhou2/article/details/131078650
9.6
今日推荐