我对《编写高质量代码》的看法
- 整本书的示例代码风格比较差,一点也不是作者看重的pythonic。空格、大小写、命名规则、单字母名称等随处可见;
- 示例代码短缺、逻辑错误比较多。例如打印素数的代码明显错误,还有几处印刷导致代码缺行的;
- 大多数主题一带而过,比较适合开阔视野,具体应用需要额外的工作;
勘误
1. 第一章:建议1:(2)代码风格
“不应当过分地使用奇技淫巧”, 但c[::-1]
并不等于list(reversed(c)
。
|
|
2. 第一章:建议1:(3)标准库
当用%
做格式化式,如果只有一个格式化参数,一般是直接格式化而不用单元素的元组。
|
|
3. 第一章:建议1:(3)标准库
format
并不比%
格式化更pythonic
。format不能用于字符串中有{}
原型的情况。另外根据pep8
的风格说明,位置参数和值之间没有空格。
|
|
一般来说,由于format
中没有提供格式化字符,需要调用参数的特殊方法,会比%
慢。
|
|
4. 第一章:建议2:(1)要避免劣化代码:2)避免使用容易引起混淆的名称
a. 两个示例代码都不pythonic
;
b. 变量名一般用C语言式的varibale_name
,而不是驼峰式的camelCase
;
c. 短作用域的临时变量用短小的名称反而比较好看;
d. 实例代码有问题,当没有找到num
时应该返回False
;
e. 代码风格问题,操作符两边应该有空,参数之间逗号之前没有空格之后有空格。
|
|
5. 第一章:建议4:4)
关于注释,编写可阅读的代码,推荐《编写可读代码的艺术》。
几个示例中的代码和注释都很糟糕。代码变量本身没有足够的意义,注释本身就是代码的重复。
6. 第一章:建议5:通过适当添加空行使代码布局更为优雅、合理
实例一的猜数代码写的并不好。
|
|
7. 第二章:建议8:利用assert语句来发现问题
其中的assert语句的表达式是错误的。
|
|
__debug__
是不能在程序中赋值,在python2.7中报SynaxError
。如果python启动时指定-O
或-OO
优化选项则__debug__
为False,此时断言会被优化掉。
使用断言要区分错误
和异常
。异常
需要用代码处理,如文件打开失败、网络连接失败、磁盘空间不足等这些属于异常
情况(CornerCase); 错误
是不应该发生的情况,预示程序存在bug。所以断言一般用于检查正常程序中不会出现,但是为了预防万一出现用于提示有bug的场合。
|
|
8. 第二章:建议10:充分利用Lazy evaluation的特性:1)避免不必要..
这种in判断不太建议使用列表或者元组,而是使用set。在python
中,set检查是O(1)而列表和元组是O(n)。
|
|
9. 第二章:建议12:不推荐使用type来进行类型检查
实例一的代码中type(n) is types.IntType
。这个n
的类型肯定不是types.IntType
,而是UserInt
(作者认为n的类型应该是int,“显然这种判断不合理这句”)。作者应该想说的是isinstance
,但isinstance(n, int)
返回的也是True
…
|
|
实例二中的isinstance("a", (str, unicode))
这个可不pythonic。在python2中检查是否是字符串用basestring
,python3中直接用str
。
|
|
10. 第三章:建议19:(2)循环嵌套导入的问题
实例代码中c1和c2的循环嵌套与使用from .. import ..
还是使用import
没有关系。python
的编译执行是按照代码块而不是逐行。当编译执行代码块中的代码引用其他模块,而其他模块又引用了当前代码块或者还未编译的代码块时就会导致循环引用。而且循环引用除去报ImportError
,还可能报AttributeError
。
|
|
11. 第三章:建议13:使用else子句简化循环
a. 使用else
是否能够pythonic
不太清楚。else
有时能够简化逻辑,但却与直觉相反会使代码不易读懂。(也许是与我自己的直觉相反,我总以为else
是条件异常也就是break
才执行;但在这里是额外的、附加的
的意思,循环正常时执行);
b. 示例代码中的print_prime
函数逻辑错误,打印给定n
内的素数不包含1以及n
本身。
|
|
12. 第三章:建议24:遵循异常处理的几点基本原则
图3-1中的图中少了一些执行流。try
异常没有被捕获而finally
没有异常时,异常会被抛出。
|
|
13. 第三章:建议26:深入理解None
因为None
、0
、[]
,()
的布尔值都为False
,所以在0
表示数字而非空的含义时,应该用is None
来区分。
|
|
14. 第三章:建议30:[],()和{}:一致的容器初始化形式
“本节开头的例子改用列表解析,直接可将代码行数减少为2行,这在大型复杂应用程序中尤为重要,…”。列表解析比较简洁清晰,但是不要过度使用(像下面这样其实很难直接看懂)。简单的追求代码短小是没有价值的,简洁清晰明确的表达算法才最重要。
|
|
字典解析式的语法是{key_exp: value_exp for iter if cond}
,中间需要冒号。
15. 第三章:建议32:警惕默认参数潜在的问题
最后一个示例代码,当然不能传入time.time()
,但是也不能传入time.time
这个函数本身。
|
|
16. 第四章:建议36:掌握字符串的基本用法
“空白符由string.whitespace常量定义”这句话表述不太正确。应该是“空白符的定义与string.whitespace默认值一样”更加贴切。简单来说改变string.whitespace
的值并不会改变字符串strip
等操作的行为。
|
|
17. 第四章:建议37:按需选择sort()或者sorted()
a. sort是列表对象自带的方法(不是函数);sorted是内建的函数。
b. sort会改变列表对象本身;sorted返回一个排好序的新对象。
17. 第五章:建议50:利用模块实现单例模式
a. 不知道为什么要用scala
和falcon
这种小众语言讲解他们的singleton
实现;
b. 利用模块做单例,见下。
|
|
18. 第五章:建议51:用mixin模式让程序更加灵活
Minxin
也是一种多重继承的实现方式。ruby
的多重继承就是mixin
,它规定一个类只能有一个普通父类,但是可以有多个minin
的父类。mixin
类有几个特点:第一不能实例化,第二不能单独使用。简单的说,minxin
类只实现了普通类的功能,不实现普通类的状态(改变)。
|
|
另外,简单的在初始化时传入行为是在python中更加好的办法(而不是动态修改基类),这叫做策略模式
。
|
|
19. 第五章:建议56:理解名字查找机制
其中嵌套作用域
的代码缺少outer
部分。
|
|
20. 第六章:建议60:区别__getattr__
和__getattribute__
a. __getattr__
是一般的属性方法查找函数,当属性方法不在instance.__dict__
或者调用方法返回AttributeError
时被调用;
b. __getattribute__
会接管一切属性方法包括特殊方法的查找,即使当前实例的属性方法存在。
21. 第六章:建议65:熟悉python的迭代器协议
a. 产生器是产生器,迭代器是迭代器。python没有强制类型,一般都过协议(接口)定义行为。产生器只不过是支持迭代器的协议有相应的接口,但不能说产生器是迭代器。
b. groupby
的代码示例有误,输出的是一个列表的列表。
|
|
21. 第六章:建议67:基于生成器的协程及greenlet
”其中的适时是多久,基本只能静态地使用经验值“。生产-消费模型中一般会使用信号量来解决等待和唤醒问题,不用人为的调用sleep
设置睡眠时间,所以也不用”静态地使用经验值“。
22. 第六章:建议68:理解GIL的局限性。
a. “默认情况下每个100个时钟“。python的100个执行指令,但是并不是严格限制,因为有些指令会有跳转等情况导致不能严格的100个指令就让出线程。确切的说是进行一次线程switch,python2.7不保证当前的线程一定会让出,全部由操作系统内核本身决定。
b. GIL存在的主要原因就是GIL会使解释器简洁而且高效。不然的话,需要将全局锁细化到每个需要同步的代码和结构中,大量锁的处理使解释器很难编写,大量锁的竞争等反而使解释器性能下降。简单来说,GIL是粗粒度
的锁,比细粒度的锁的优势更大(一种折中方案)。
23. 第八章:建议87:利用set的优势
“1)向list和set中添加元素,…list的耗时约是set的89倍“。这个结论有误,原因在于测试代码中增加了查找操作”x in tmp“,list中的查找是O(n),set是O(1),而不是添加元素的耗时差距。实际上,由于set是哈希表需要计算添加元素的哈希值并且需要调用冲撞函数,一般比list的直接添加元素需要更多额外操作的时间。
|
|